Rust 程序设计语言 第一印象

首先看这个:

C++中在 C++14 之后可以使用 ' 来分隔数字字面量。而 Java 和 Rust 在设计阶段很早的时候就支持了用 _ 来分隔数字。

个人感受:

  • 处处充斥强制移动语义。相关的是 Drop Trait,而基本类型还实现了 Copy Trait。(所有权和 C++ RAII 想要解决的问题相似)
  • 默认定义是常量,包括引用默认是常引用。(和 C++ 相反)
  • 允许同一个作用域内 shadow,试图把变量名当成真正的标签来用(类似 Python)。
  • 错误处理用 expect,比 try-catch 简洁。
  • 内置元组和 range、if 条件不需要加括号。
  • 很多实用包都得用 crate,在标准库中没提供……

认识所有权

同一生存期,对对象 A 的可变引用将排斥任何对对象 A 的其他引用。当且仅当所有引用都是常量引用时编译才能够通过。一个引用的作用域从声明的地方开始一直持续到最后一次使用为止。编译器能够把这一点检查出来。如果最后一次使用结束了,就不参与和其他引用的排斥行为了。

Note

str 类型好像见不到,只能见到 str& 类型。由于是引用,它自己没有所有权。

String 类型动态分配空间,String 这样类型的赋值行为都是移动语义。要创建一个拷贝,需要使用 clone() 方法。

String 切片引用得到的类型是 &str

fn first_word(s: &String) -> &str {
    let bytes = s.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }
    &s[..]
}

fn main() {
    let mut s = String::from("hello world");
    let word = first_word(&s);
    s.clear(); // 错误!
    println!("the first word is: {}", word);
}

因为 first_word 使用了 s 的不可变引用,返回的结果保存在 word 中。所以认为 word 也带有了对 s 的不可变的引用。在 word 生命周期结束之前,clear()s 要求的可变引用是不会通过编译的。这就解决了悬挂指针等问题。

link

由于一个 &String 自动是一个 &str,所以写接口的时候用 &str 更加通用。

结构体

定义和使用

粗看上去和 Swift 很相似。

let user2 = User {
    email: String::from("another@example.com"),
    ..user1
};

用结构体更新语法从 user1 中原样保存其他值(这些值中包含一个 Drop 语义的 String,在这里没有贴出)到 user2 中之后user1 就不能再被使用了。但如果赋值的值全都是 Copy 语义的,user1 则能够继续被使用。

用元组定义结构体:

struct Color(i32, i32, i32);

其实相当于一个元组类型的别名,但是还同时有了类型检查。

结构体中不能够定义没有所有权的类型。比如不能捕获 &str,只能使用 String。第十章讲了解决这个问题的方法。

结构体方法

这是一个方法,可以在实例后用.来调用。

这种方法叫做关联方法。使用结构体名和  ::  语法来调用这个关联函数:比如  let sq = Rectangle::square(3);。这个方法位于结构体的命名空间中:::  语法用于关联函数和模块创建的命名空间。

impl 就像 extension。可以有多个 impl 块。

打印

{}是 Display,{:?}是 debug 打印,{:#?}是 pretty-print。

另一种使用  Debug  格式打印数值的方法是使用  [dbg!  宏](https://doc.rust-lang.org/std/macro.dbg.html)。dbg!  宏接收一个表达式的所有权,打印出代码中调用 dbg! 宏时所在的文件和行号,以及该表达式的结果值,并返回该值的所有权。调用  dbg!  宏会打印到标准错误控制台流(stderr)。

下面这个例子:

最后一句传的是引用。因为我们没有接收含有所有权的返回值,如果传递原值会导致所有权丢失。