于是这一篇学习笔记就以注意点的方式来写好了~
1. 变量默认都是 immutable
首先 Rust 中定义变量默认都是 immutable 的,如果想要之后修改变量的值的话,粗略来说有 2 种方式(实际上其实是 1 种)
第 1 种则是在声明变量时添加修饰符 mut,第 2 种是再次声明一个同名变量,将之前的 shadow 掉。当然,第 2 种其实并没有真正修改前一个变量在内存中的值,所以站在内存的角度来看,只有前一种方法才算真正修改了。
fn main() { let p = 100; println!("p = {}", p); p = 101; // 这里会报错 println!("p = {}", p); }
可以看到 rustc 给我们的建议是给变量 p 增加修饰符 mut,即
fn main() { let mut p = 100; println!("p = {}", p); p = 101; println!("p = {}", p); }
当然也可以使用第二种方法~Shadow
fn main() { let mut p = 100; println!("p = {}", p); let p = 101; println!("p = {}", p); }
这里第二次声明变量 p 的时候,实际上是把第一个变量 p 给 shadow 掉了,类似于 C/C++ 中局部变量 shadow 掉全局变量一样w 而实际上被 shadow 的那个变量在内存中的值并没有改变
不过需要注意的是,声明为了 mutable 之后,该变量的类型就不可发生改变了(除非再次被 shadow 成别的);但是第二种 shadow 的话,变量的类型是可以发生改变的
fn main() { let p = "string"; println!("p = {}", p); let mut p = 100; println!("p = {}", p); // mutable 只是值可以发生变化 // 并非表示弱类型 p = "error"; println!("p = {}", p); }
2. 一些基本数据类型
这个好像没有太多可以记笔记的,,,
对于整数来说,前缀 i 代表有符号数,前缀 u 则为无符号数。Rust 中整数的有如下类型(下表来源:https://doc.rust-lang.org/book/ch03-02-data-types.html)
Length | Signed | Unsigned |
---|---|---|
8-bit | i8 | u8 |
16-bit | i16 | u16 |
32-bit | i32 | u32 |
64-bit | i64 | u64 |
128-bit | i128 | u128 |
arch | isize | usize |
最后一个 arch 则是根据编译时的操作系统是 64 位还是 32 位来决定的
浮点数就很简单了,要么单精度 f32,要么双精度 f64
然后还有一个 bool 类型,这个是必然的
最后就是 Tuple 和 Array 了~Array 跟 Python 中的写法几乎是一样的~Tuple的话,略有一点不同,在取其中一个的时候,可以通过如下方式
fn main() { let t = ("Hello", 233, 1.3); println!("{}", t.2); }
3. 函数、控制流
说实话,函数的写法的话,几乎跟 Swift 没有多大的区别,if、for、loop、while 也都是大同小异,让我觉得 Rust 里这部分比较值得一提的是,可以用 break 返回一个值到循环体外
fn main() { let mut counter = 0; let result = loop { counter += 1; if counter == 5 { break counter * 10; } }; println!("result is {}", result); }
4. Ownership
简单来说,对于堆上的对象,=(Assign) 默认是传引用,对于栈上的数据,则都是复制值。
如果要 deep copy 一个堆上的对象,可以使用 .clone() 方法(或者自己实现)
对象默认在函数间传递时,会将其 ownership 一同传递。
fn main() { // s 对象在堆上被申请 let s = String::from("hello"); // s 的值与其 ownership 移动到了函数中 takes_ownership(s); // 因此 s 在那之后不再有效 // 下面一行会报错 println!("s is gone... {}", s); } fn takes_ownership(some_string: String) { // some_string 开始的作用域 println!("{}", some_string); } // some_string 在此处离开作用域 // `drop` 被调用 // some_string 被释放
当然,ownership 既可以被拿走,也可以从别处被给予~
fn main() { // s1 对象在堆上被申请 let s1 = String::from("hello"); // s1 的值与其 ownership 移动到了函数中 // 而这个函数又将其结果的 ownership 给回了 s2 let s2 = takes_and_gives_back(s1); // 因此 s1 在那之后不再有效 // 但 s2 是有效的 println!("s2 is {}", s2); } fn takes_and_gives_back(some_string: String) -> String { // some_string 开始的作用域 some_string } // some_string 被返回 // 其值与 ownership 也被返回
5. 引用
总是传递值与 ownership 也是很麻烦的,因此 Rust 里也是可以传引用的,简单来说,与 C++ 类似,只需要加上 & 即可~当然 Rust 不是 C++,只用 & 的话,传的其实是 immutable 的引用
fn main() { let s = String::from("hello"); change(&s); } fn change(some_string: &String) { // immutable! some_string.push_str(", world"); }
要变成 mutable 的引用的话,只需要加上 mut 修饰即可w 不过这个 mut 修饰需要同时在变量声明、函数声明、传参时出现~
fn main() { let mut s = String::from("hello"); change(&mut s); } fn change(some_string: &mut String) { // mutable~ some_string.push_str(", world"); }
还有一点与 C++ 不同的是,Rust 为了避免数据竞态的问题,immutable 的引用可以同时有多个,因为都只是读取;而 mutable 的引用,必须要在 immutable 的使用完之后,且一次(一个作用域内)只允许有一个对同一对象的 mutable 引用
6. Slices
这个切片也跟 Python 中的类似,略有不同的是一下几处
首先是不支持负值,也就是说没办法像 Python 那样使用 -1 去拿最末尾的元素
其次的话,Rust 使用的是 .. 而不是 Python 中的 :
fn main() { let s = String::from("hello"); let slice = &s[1..3]; println!("slice {}", slice); let slice = &s[3..]; println!("slice {}", slice); let slice = &s[..]; println!("slice {}", slice); }
相同的地方的话,基本上也能看出来:1) 都是左闭右开区间,2) 都可以省略起始或结束或两者同时省略,3) 其 slices 都是只是原始对象的一个 view,并没有创建新的对象
因此,又由于 Rust 默认是 immutable 的,所以在 immutable 的 slice 使用完之前,原始对象都不能有 mutable 的操作或引用出现(即使在声明时有 mut 修饰也不行)
fn main() { let mut s = String::from("hello"); let slice = &s[3..]; // error: mutable operation while immutable slice still valid s.clear(); println!("slice {}", slice); }
7. 结构体
结构体的话,其实写起来用起来感觉跟 Swift 差不多,
struct User { username: String, email: String, sign_in_count: u64, active: bool, } fn main() { let mut user1 = User { email: String::from("[email protected]"), username: String::from("someusername123"), active: true, sign_in_count: 1, }; user1.email = String::from("[email protected]"); // 要利用已有的结构体的数据的话 // 可以只显式写出来不同的部分 let user2 = User { email: String::from("[email protected]"), username: String::from("someusername456"), // 剩余的部分可以使用 ..user1 来自动复制 ..user1 }; } fn build_user(email: String, username: String) -> User { // 最后就是如果变量名与结构体中的 field 名字相同的话 // 可以用如下方式少写一些 User { email, username, active: true, sign_in_count: 1, } }
暂且来说,结构体这边没有非常与众不同的部分~