先上学习链接 Rust语言圣经
变量定义
Rust的赋值称为变量绑定,对于rust来说,所有的内存都是有主人的,而且一般情况下完全属于它的主人。
let x = 1; ///<将1绑定到x上
在rust中变量默认是不可变的,除非你使用mut 关键字人变量变为可变的。这看起来和常量很像,但还是有所不同,常量在编译完成后以及确定了它的值,它是自始至终不可改变的。
const MAX_POINTS: u32 = 100_000; ///常量
let x = 5; ///<不可变变量
let mut y = 4; ///<可变变量
可以通过使用下划线开头忽略未使用的变量,这样编译器就不会产生警告。
fn main() {
let _x = 5;
let y = 10;
}
rust允许声明相同的变量,在后面声明的变量会遮蔽掉前面的。
fn main() {
let x = 5;
// 在main函数的作用域内对之前的x进行遮蔽
let x = x + 1;
{
// 在当前的花括号作用域内,对之前的x进行遮蔽
let x = x * 2;
println!("The value of x in the inner scope is: {}", x);
}
println!("The value of x is: {}", x);
}
类型介绍
Rust 每个值都有其确切的数据类型,总的来说可以分为两类:基本类型和复合类型
基本类型一般有以下几部分
- 数值: i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, f32, f64
- 字符串: 字面量和切片
- 布尔类型: true和fasle
- 字符类型:表示单个Unicode字符,存储为4个字节
- 单元类型:()
Rust会根据上下文自动推导变量类型,但是当无法推导时需要手动给一个类型标注
//let guess = "42".parse().expect("Not a number!");
let guess: i32 = "42".parse.expect("Not a number!");
let guess = "42".parse::<i32>().expect("Not a number!");
数值类型
数值类型的定义和其他语言没什么不同,isize和usize分别取决于计算机的CPU类型。
与其他语言不同的是当出现整型溢出的时候,在debug模型下Rust会抱错,但是当使用--release编译时,rust不会抱错。对于这一部分处理可以通过显式的处理方式处理可能的溢出。
- 使用
wrapping_*方法在所有模式下都按照补码循环溢出规则处理,例如wrapping_add - 如果使用
checked_*方法时发生溢出,则返回None值 - 使用
overflowing_*方法返回该值和一个指示是否存在溢出的布尔值 - 使用
saturating_*方法使值达到最小值或最大值
fn main(){
let a: u8 = 255;
let b = a.wrapping_add(20);
println!("{}", b);
}
由于浮点数的底层格式的特殊性,我们应该尽量避免两个浮点数相等的比较,同时Rust的HashMap数据结构无法使用浮点数作为key值。
来看一段崩溃的代码
fn main() {
// 断言0.1 + 0.2与0.3相等
assert!(0.1 + 0.2 == 0.3);
}
因为二进制进度的问题,0.1 + 0.2 并不严格等于0.3,我们可以使用类似的方式来替换以上代码实现一个浮点的相等比较
fn main {
assert!((0.1_f64 + 0.2 - 0.3).abs() < 0.000001);
}
对于数学上未定义的结果Rust会产生一个NaN, 可以通过is_nan()等方法来判断一个数值是否是NaN:
fn main() {
let x = (-42.0_f32).sqrt();
if x.is_nan() {
println!("未定义的数学行为")
}
}
在rust中,对应较长的字符可以用_进行分割,提升可读性。
let one_million: i64 = 1_000_000;
Rust可以通过1...5的方式生成从1到4的连续数字,使用1...=5的方式生产1到5的连续数字
for i in 1..=5 {
println!("{}",i);
}
在这里推荐一个社区开发的高质量数值库:num
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)
}
字符、布尔、单元类型
在rust中,字符为4字节编码的unicode,bool和其他语言一样只有true和false,单元类型()不占空间,单元类型在rust中作为很多函数的返回值。对于没有返回值的函数,在rust中有单独定义: 发散函数。
语句和表达式
Rust 的函数是由一系列语句组成,最后由一个表达式来返回值。如果函数没有返回任何值,会隐式的返回一个()。
fn add_with_extra(x: i32, y: i32) -> i32 {
let x = x + 1; // 语句
let y = y + 5; // 语句
x + y // 表达式
}
所有权和借用
所有权原则
- rust中每一个值都能被一个变量所拥有,该变量称为值的所有者
- 一个值同时只能被一个变量所拥有
- 当所有者离开作用域范围时,这个值将被丢弃
在rust中对于基本类型的互相赋值会通过拷贝的形式进行,而对于其他类型则通过转移所有权的方式进行。
let s1 = String::from("hello");
let s2 = s1;
在这段代码中,s1的值的所有权转移到了s2上,s1不再有效,既s1被移动到了s2中。而如果想要s2不被释放,需要使用深拷贝的方式将数据拷贝到s1上,可以通过clone方法来实现
let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1 = {}, s2 = {}", s1, s2);
在rust中有一个copy的特征,如果一个类型拥有copy特性,一个旧的变量在被赋值给其他变量后仍然可用。以下是一些具有copy特性的类型
- 所有整数类型,比如
u32 - 布尔类型,
bool,它的值是true和false - 所有浮点数类型,比如
f64 - 字符类型,
char - 元组,当且仅当其包含的类型也都是
Copy的时候。比如,(i32, i32)是Copy的,但(i32, String)就不是 - 不可变引用
&T,例如转移所有权中的最后一个例子,但是注意: 可变引用&mut T是不可以 Copy的
将值传递给函数一样会发生移动或者赋值,同let语句一样。
使用某个变量的指针或者引用,再Rust中通过借用这个概念来完成。
fn main() {
let x = 5;
let y = &x;
assert_eq!(5, x);
assert_eq!(5, *y);
}
上述代码使用&运算符来借用了x的值,使用*来解出借用的值。
但是正如默认不可变一样,引用指向的值默认也是不可变的,如果要改变引用的值,需要用到可变引用。
fn main() {
let mut s = String::from("hello");
change(&mut s);
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
}
/*
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s; ///<这里会报错,在rust中同一个作用域,特定数据只能有一个可变引用
println!("{}",r1); ///<如果把这行注释掉就不会报错,编译器会在引用最后一次使用的地方把它释放掉
*/
总结
通过以上我们了解了rust的一些基本规则:变量的定义、变量类型、语句和表达式、所有权和借用。后面将学习一些高级数据结构的使用方式。

2565

被折叠的 条评论
为什么被折叠?



