从零开始的 Rust 学习笔记(2)

于是这一篇学习笔记就以注意点的方式来写好了~

  1. 变量默认都是 immutable
  2. 一些基本数据类型
  3. 函数、控制流
  4. Ownership
  5. 引用
  6. Slices
  7. 结构体

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

LengthSignedUnsigned
8-biti8u8
16-biti16u16
32-biti32u32
64-biti64u64
128-biti128u128
archisizeusize

最后一个 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,
    }
}

暂且来说,结构体这边没有非常与众不同的部分~

声明: 本文为0xBBC原创, 转载注明出处喵~

Leave a Reply

Your email address will not be published. Required fields are marked *