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
)。
下面这个例子:
最后一句传的是引用。因为我们没有接收含有所有权的返回值,如果传递原值会导致所有权丢失。