Rust简明教程第一章-easy rust

观看B站软件工艺师杨旭的rust教程学习记录,有删减有补充

hello word

文件按名为main.rs,main是函数入口,rust默认推断为void main,!代表println是一个宏,函数没有

rust 复制代码
fn main(){
    println!("{}"," Hello world!");
}

编译(仅适合小项目,一般用Cargo)

rust 复制代码
rustc main.rs

执行

rust 复制代码
#win
.\main.exe
#Linux
./main

Cargo

查看cargo版本cargo --version

创建rust项目

rust 复制代码
cargo new hello_rust

运行项目

rust 复制代码
cargo run

检查代码但不生成可执行文件,速度比构建快,能干过编译器就是好代码

rust 复制代码
cargo check

构建项目,生成的可执行文件在/target/debug/build

rust 复制代码
cargo build

为发布构建,编译时进行优化但编译时间更长,生成的可执行文件在/target/release/build

rust 复制代码
cargo build --release

详细命令

自定义发布配置

cargo build:采用dev配置

cargo build --release:采用release配置

在Cargo.toml中添加profile可以覆盖配置

toml 复制代码
[prifile.dev]
opt-level = 0   	#开发时使用最低优化级别
[profile.release]
opt-level = 3   	#发布时使用最高优化级别

生成更小的可执行文件

toml 复制代码
[profile.release]
opt-level = "z"       # 优化代码尺寸
lto = true            # 启用链接时优化
codegen-units = 1     # 降低代码生成单元数,增加优化时间但减少二进制大小
strip = "debuginfo"   # 移除调试信息

生成更快的可执行文件

toml 复制代码
[profile.release]
opt-level = 3         # 最大程度优化代码速度
lto = "fat"           # 启用最大程度的链接时优化
codegen-units = 1     # 降低代码生成单元数,增加优化时间但提升性能

其他配置

Rust依赖库

https://crates.io/

Cargo.toml添加需要的库,^代表与指定版本兼容的版本,如

rust 复制代码
[dependencies]
rand = "^0.8.5"

更新cargo.lock版本

rust 复制代码
cargo update

猜数游戏

rust 复制代码
use rand::Rng;
use std::cmp::Ordering;
use std::io; //trait
fn main() {
    println!("猜数!");
    //生成1~100的随机数
    let secret_number = rand::thread_rng().gen_range(1..101);
    println!("神秘数值是{}", secret_number);
    loop {
        println!("猜一个数");
        let mut guess = String::new();
        //等价于std::io
        io::stdin().read_line(&mut guess).expect("无法读取行");
        //去掉两边的空白并转换为整数,变量遮蔽shadow,match处理错误
        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,     //成功就返回num
            Err(_) => continue, //_代表不关心它的值是什么
        };
        println!("你猜的数字是:{}", guess);

        match guess.cmp(&secret_number) {
            Ordering::Less => println!("太小"),
            Ordering::Greater => println!("太大"),
            Ordering::Equal => {
                println!("猜对了");
                break;
            }
        }
    }
}

变量与可变性

  • 使用let声明变量
  • 变量赋值在Rust中被称为变量绑定
  • 变量默认是不可变的
  • 使用mut关键字使变量可变
rust 复制代码
fn main() {
    let a = 5;
    let mut b = 4;
    b = 6;
    println!("a:{},b:{}", a, b);//a:5,b:6
}

变量与常量

常量声明后不可变

  • 常量使用const关键字声明,它的类型必须标注
  • 不可以使用mut,常量永远不可变
  • 常量可以在任何作用域声明,包括全局作用域
  • 常量只可以绑定到常量表达式,无法绑定到函数的调用结果或只能在运行时计算出的值

命名规范 :Rust常量使用全大写字母,每个单词之间使用_分开,如MAX_VALUE

rust 复制代码
const MAX_VALUE: u32 = 1_000;
fn main() {
    const MIN_VALUE: u32 = 1_000;
    println!("最大值:{}\n最小值{}", MAX_VALUE, MIN_VALUE);
}

shadowing(变量遮蔽)

后面声明的变量会遮蔽前面声明的

rust 复制代码
fn main(){
    let a=5;
    let a=a+3;
    println!("{}",a);//8
}

用法

rust 复制代码
fn main() {
    let str = "hello world";
    let str = str.len();
    println!("{}",str);
}

字面量

字面量是用于表达源代码中一个固定值的表示法(notation)。几乎所有计算机编程语言都具有对基本值的字面量表示,诸如:整数、浮点数、布尔值以及字符串(String类型不是字符串!)

它们的共同特点就是值固定,存储在stack(栈内存)上

let a = 4;//4是字面量,a存储实际字面值
let s = "Hello";//Hello是字面量,s存储实际字面值
let b = true;//true是字面量,b存储实际字面值
let f = 3.14;//3.14是字面量,f存储实际字面值

String类型是可变的,hello被转换为String类型,hello可变,数据存储在heap(堆内存)上,指针(长度、容量)存储在stack(栈内存)上

  • 字面值是不可变的,可变就不叫字面值

    let s1 = "hello";//hello是字面量,s1存储的是对hello的引用&str,s1本质是一个字符串切片
    let s2 = String::from("hello");//hello是字面值,s2存储hello字面量的所有权,也就是hello的指针

数据类型

Rust是静态编译语言,在编译时必须知道所有变量的类型

  • 基于使用的值,编译器通常能推断出它的具体类型
  • 可能的类型较多时必须标注类型(如需要将string转换为integer)
rust 复制代码
fn main() {
    let string_to_integer: u32 = "2024".parse().expect("不是数字");
    println!("{}", string_to_integer); //2024
}

标量类型

  • 整数类型
  • 浮点类型
  • 布尔类型
  • 字符类型

integer 整型

  • 有符号范围:-2^n-1~2^n-1 -1
  • 无符号范围:0~2^n -1
长度 有符号类型integer 数值范围 无符号类型unsigned 数值范围
8位 i8 -128~127(-2^7~2^7-1) u8 0~255(2^8-1)
16位 i16 -32768~32767(-2^15~2^15 -1) u16 0~65535(2^16 -1)
32位 i32 -2^31~2^31 -1 u32 0~2^32 -1
64位 i64 -2^63~2^63 -1 u64 0~2^64 -1
128位 i128 -2^127~2^127 -1 u128 0~2^128
视架构而定 isize cpu是n位就是-2^n-1~2^n-1 -1 usize cpu是n位就是0~2^n -1

rust整型默认使用i32

rust 复制代码
use std::any::type_name_of_val;
fn main() {
    let num1 = 18;
    let num2 : i64 = 18;
    println!("类型: {}", type_name_of_val(&num1));//类型: i32
    println!("类型: {}", type_name_of_val(&num2));//类型: i64
}

整数字面值

字面量 例子
Decimal十进制 12_000
Hex十六进制 0xff
Octal八进制 0077
Binary二进制 0b1111_1101
Byte(u8 only)字节 b'A'

整数溢出

u8最大为255,对于超出数据范围的操作,rust会报错将会溢出

rust 复制代码
fn main(){
    let a : u8 = 255;
    let b = a+4;//error:attempt to compute `u8::MAX + 4_u8`, which would overflow
    println!("{}",b);
}

使用arithmetic_overflow显式处理表明我们需要它按补码循环的方式溢出,"环绕"操作

rust 复制代码
fn main(){
    let a:u8 = 255;
    let b= a.wrapping_add(20);//按照补码循环溢出规则处理
    println!("{}",b);//19
    let b = a.checked_add(20);//发生溢出,则返回 None
    println!("{:?}",b);//None
    let b = a.overflowing_add(20);//返回该值和一个指示是否存在溢出的布尔值
    println!("{:?}",b);//(19,true)
    let b = a.saturating_add(20);//限定计算后的结果不超过目标类型的最大值或低于最小值
    println!("{}",b);//255
}

std::num::Wrapping里还有其他用法

浮点类型

精度 正浮点数范围 负浮点数范围
f32 1.4.13e-45~3.4028e38 -3.4028e38~-1.4013e-45
f64 2.2251e-3.08~1.7977e3.8 -1.7977e308~-4.9407e-324
  • 单精度:符号位:1位 指数部分:8位 尾数部分:23位
  • 双精度:符号位:1位 指数部分:11位 尾数部分:52位

现代计算机处理器对两种浮点数计算的速度几乎相同,但 64 位浮点数精度更高,所以rust浮点型默认64位

rust 复制代码
use std::any::type_name_of_val;
fn main(){
    let a = 2.0;
    let b:f32 = 2.0;
    println!("类型: {}", type_name_of_val(&a));//类型: f64
    println!("类型: {}", type_name_of_val(&b));//类型: f32
}

浮点数往往是预期数字的近似表达,0.1+0.2居然不等于0.3!

rust 复制代码
fn main(){
    let a=0.1;
    let b=0.2;
    println!("{}",a+b);//0.30000000000000004
}

NaN (not a number)

不是一个数,NaN 是一种特殊的浮点数值,用于表示无效或未定义的操作结果

将a的值设置为f64类型的NaN

rust 复制代码
fn main() {
    let a: f64 = f64::NAN;
    if a.is_nan() {
        println!("a 不是一个数 (NaN)");
    }
}

类型转换

  • 类型不同的数不允许比较,可以显式转换为两者中更高的类型
  • 类型转换必须显式转换
rust 复制代码
fn main() {
    let a : i8 = 4;
    let b : i32 = 8;
    //将a转换为i32
    println!("{}",i32::from(a)<b);//true
}

使用as转换

rust 复制代码
fn main() {
    let a : i8 = 4;
    let b : i32 = 8;
    let char = 'a';
    println!("{}", ((a as i32) < b));//true
    println!("{:?}",char as i8);//97
}

数值运算

rust 复制代码
fn main(){
    let a = 2;
    let b = 3;
    println!("{}",a+b);//5
    println!("{}",a-b);//-1
    println!("{}",a*b);//6
    println!("{}",a/b);//0
    println!("{}",a%b);//2
}

位运算

运算符 解释
& 位与 相同位置均为1时则为1,否则为0
|位或 相同位置只要有1时则为1,否则为0
^ 异或 相同位置不相同则为1,相同则为0
! 位非 把位中的0和1相互取反,即0置为1,1置为0
<< 左移 所有位向左移动指定位数,右位补0
>> 右移 所有位向右移动指定位数,带符号移动(正数补0,负数补1
>>>无符号右移 所有位向右移动指定位数,但都补0

计算机采用补码计算,正数的补码就是原码,负数的补码是原码所有位取反再加1

rust 复制代码
fn main(){
    //补码表示
    let a:i8=-2;//1111_1110 负数补码取反再+1:1000_0010->1111_1101+1->1111_1110
    let b:i8=3;//0000_0011  正数补码就是本身的2进制数
    //正数补码结果就是原码,负数补码结果-1再取反才是原码
    println!("{}",a&b);//2  0000_0010
    println!("{}",a|b);//-1 1111_1111->1111_1110->1000_0001
    println!("{}",a^b);//-3 1111_1101->1111_1100->1000_0011
    println!("{}",!a);//1   0000_0001
    println!("{}",!b);//-4  1111_1100->1111_1011->1000_0100
    println!("{}",a<<2);//-8    1111_1000->1111_0111->1000_1000
    println!("{}",a>>2);//-1    1111_1111->1111_1110->1000_0001
}

有理数和复数运算

rust 复制代码
use num::complex::Complex;
fn main() {
   let a = Complex { re: 2.1, im: -1.2 };
   let b = Complex::new(11.1, 22.2);
   let result = a + b;
   println!("{} + {}i", result.re, result.im)
 }

布尔类型

  • 占用1字节
  • 符号为bool
rust 复制代码
fn main() {
    let a = true;
    if a {
        println!("a是对的!");
    }
}

单元类型

单元类型(unit type),没有具体的数值,在没有返回值或不需要传输有意义的数据时使用

以下例子print_hello返回的数值为(),表示没有返回值,rust函数最后一行默认返回表达式的值,没有值可以返回就是(),main函数返回值也是()

rust 复制代码
fn main() -> (){
    let result = print_hello();
    println!("函数返回值: {:?}", result);//函数返回值: ()
}
fn print_hello() {
    println!("Hello, world!");//Hello, world!
}

never !类型

rust 复制代码
fn never_returns() -> ! {
    panic!("无返回值");
}
fn main() {
    let result: i32 = never_returns();
    println!("结果: {}", result);//无返回值
}

字符类型 char

  • 字符类型使用'
  • 占用4字节

所有的Unicode值都是字符,例如unicode编码的表情,Unicode都是4字节编码,其他字符

rust 复制代码
fn main() {
    let a = 'A';
    let b = '学';
    let emo = '🔞';
    println!("{},{},{}", a, b, emo);//A,学,🔞
    println!("字符占用了{}字节的内存大小",std::mem::size_of_val(&a));//4
    println!("字符占用了{}字节的内存大小",std::mem::size_of_val(&b));//4
    println!("字符占用了{}字节的内存大小",std::mem::size_of_val(&emo));//4
}

类型别名

  • 为现有类型生产另一个名称
  • 并不是独立的类型
  • 用于减少代码字符重复
rust 复制代码
type UserId = u32;
fn main() {
    let user_id: UserId = 42;
    println!("User ID: {}", user_id); //User ID: 42
}
rust 复制代码
struct Point<T> {
    x: T,
    y: T,
}
type PointF32 = Point<f32>;
fn main() {
    let point: PointF32 = PointF32 { x: 1.5, y: 2.5 };
    println!("Point: ({}, {})", point.x, point.y);//Point: (1.5, 2.5)
}

复合类型

Tuple 元组

  • Tuple可以将多个类型的多个值放在一个类型里
  • Tuple的长度是固定的,一旦声明就无法改变
rust 复制代码
fn main() {
    let tup:(i32, f64, char) = (200, 3.14, '🤣');
    println!("{},{},{}", tup.0, tup.1, tup.2);//200,3.14,🤣
    //解构赋值
    let (x,y,z) = tup;
    println!("{},{},{}",x,y,z);//200,3.14,🤣
}

Array 数组

  • 数组将多个类型相同的值放在一个类型里
  • 数组的长度固定
  • 数组的数据存放在stack(栈)上(因为它元素数量固定)
rust 复制代码
fn main(){
    let arry1 = [1,2,3,4,5];
    println!("arry1[0]:{}",arry1[0]);//arry1[0]:1

    let arry2=[4;5];//5个4
    println!("{}",arry2.len());//5

    let arry3:[i32;10] = [1;10];//10个1
    println!("arry2[0]:{},arry2的长度:{}",arry3[0],arry3.len());//arry2[0]:1,arry2的长度:10
    println!("在栈中占用字节数:{}",std::mem::size_of_val(&arry3));//40

    //数组可以自动被借用为slice切片
    println!("{:?}",&arry3[0 .. 4]);//[1, 1, 1, 1]
}

函数

  • fn关键字声明函数
  • 函数参数必须声明类型
  • Rust使用snake_case命名规范
    • 所有字母小写,单词之间用_隔开

函数无论定义在main的前面后面都没有区别

rust 复制代码
fn main(){
    println!("main函数");//main函数
    another_function();
}
fn another_function(){
    println!("其他函数");//其他函数
}

语句和表达式

  • 语句是执行动作的指令,不返回值,一般以;结尾
  • 函数的定义也是语句
  • 表达式会计算产生一个值,没有;
rust 复制代码
fn main() {
    let result = add(1, 2);//语句
    println!("{}", result);//3,语句
}
fn add(a: i32, b: i32) -> i32 {
    a + b//表达式
}

函数返回值

  • ->后边声明返回值的类型,不可以为返回值命名
  • Rust返回值是函数体最后一个表达式的值
  • 提前返回使用return关键字

前面说到最后一行没有表达式的值默认返回()单元类型,也可以使用return关键字提前返回

rust 复制代码
fn main() {
    let result = add(1, 2);
    println!("{}", result);//3
}
fn add(a: i32, b: i32) -> i32 {//->后面代表函数的返回值类型
    return a + b;//语句,虽然计算了值也有返回值,但这个返回值是a+b的结果,不是return a+b的返回值
}
相关推荐
码蜂窝编程官方7 分钟前
【含开题报告+文档+PPT+源码】基于SpringBoot+Vue的虎鲸旅游攻略网的设计与实现
java·vue.js·spring boot·后端·spring·旅游
hccee20 分钟前
C# IO文件操作
开发语言·c#
hummhumm25 分钟前
第 25 章 - Golang 项目结构
java·开发语言·前端·后端·python·elasticsearch·golang
J老熊35 分钟前
JavaFX:简介、使用场景、常见问题及对比其他框架分析
java·开发语言·后端·面试·系统架构·软件工程
AuroraI'ncoding42 分钟前
时间请求参数、响应
java·后端·spring
zmd-zk1 小时前
flink学习(2)——wordcount案例
大数据·开发语言·学习·flink
好奇的菜鸟1 小时前
Go语言中的引用类型:指针与传递机制
开发语言·后端·golang
Alive~o.01 小时前
Go语言进阶&依赖管理
开发语言·后端·golang
花海少爷1 小时前
第十章 JavaScript的应用课后习题
开发语言·javascript·ecmascript
手握风云-1 小时前
数据结构(Java版)第二期:包装类和泛型
java·开发语言·数据结构