项目管理
cargo 用于管理项目.
cargo new xxx # 创建一个 xxx 的项目
cargo build # 编译并构建出可执行文件
cargo update # 更新
cargo run # 构建并运行
cargo check # 检查是否有语法错误
变量可以被 shadow, 也就是屏蔽之前的变量, 弄一个新的变量
let mut a
, 变量默认是 immutable 的, 如果用了 mut
修饰则是 mutable 的.
.toml 这个文件用于管理依赖和依赖的版本.
let guess: u32
, 标识你想得到一个无符号的 32 位的整数.
for 循环用的是 loop
, break
可以跳出循环
loop {
match guess.cmp(&secret_number) {
Ordering::Less => println! ("Too small!"),
Ordering::Greater => println! ("Too big!"),
Ordering::Equal => {
println! ("You win");
break;
}
}
}
分支匹配还可以用 match
:
match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
}
返回的结果是一个枚举, Result
常量
const THREE_HOURS_IN_SECOND: u32 = 60 * 60 * 3;
Rust 中的数据在运行时会有越界检查.
Rust 是一门静态类型的语言, 嗯, 我就喜欢静态的, 不喜欢动态的.
Rust 中的函数定义和调用之间没有先后顺序, 只要你在某个位置定义了就行.
函数的参数定义为: 形参名: 类型
他妈的, 为参数名和参数类型之间要有分号, 这他喵的不是多打字吗?
Rust 中的 statement 和 expression 并不是一个东西, statement 没有返回值, expression 有返回值的. expression 可以是 statement 的一部分.
调用宏也是一个 expression, 花括号包裹的域也是一个 expression.
let y = {
let x = 3;
// 卧槽, 这个地方还不能用分号
x + 1
};
表达式并不包括结尾的分号.
表达式加了分号就变成了 statement.
Rust 这个 if 怎么和 go 挺像的?
Rust 的 if 只会执行第一个条件为 true 的 block, 后面的其他即使为 true 也不再执行了.
突然感觉 match 和 switch 还挺像的?
let number = if condition { 5 } else { 6 };
不太喜这种形式, 还是喜欢三元表达式, 另外这里的 if 和 else 的类型应该一致.
为啥要一致, 是为了让 Rust 在后续的编译时可以做类型校验, 如果 Rust 不知道的话就做不了类型校验了.
可以在 loop 前指定标签 (字符串, 'counting_up: loop {}
), 然后 break label
, 就可以直接跳出来了.
loop 还可以作为返回值.
ownership
好像到了最精彩的环节了.
- ownership
- borrowing
- slices
- memory layout
所有权是 Rust 管理内存的一套规则. Rust 使用了一套编译器可以检查的所有权系统的规则来管理内存. 如果违反了任意一项规则, 那么程序将不会被编译. 并且所有权的这个功能不会拖慢系统的运行.
因为所有权对于很多的程序员来说是一套全新的规则, 所以确实要花时间来适应. 如果你越来越适应 Rust, 那么你会很自然地开发出安全且高效的代码.
当你你理解了所有权, 你将对 Rust 的这个特有的属性有更加坚实的认识. 这里将通过 strings 的例子带你学习 Rust 的所有权.
堆和栈
push 到 stack 要比 heap 要快, 因为分配器不用去寻找可用的空间, 数据总是存在栈顶.
所有权规则
- 在 Rust 中的每个值都有一个变量, 被称为所有者 (owner)
- 同一时间只有一个所有者
- 当所有者离开了域, 这个值将会被 drop 掉
变量的域
当一个变量离开了作用域, Rust 将会调用一个特殊的函数, 这个函数被称之为 drop
.
在变量的生命周期结束, C++ 也会释放资源, 这被称之为 RAII (Resource Acquisition Is Initialization).
这种模式对于 编写 Rust 代码而言有着深远的影响.
move
let s1 = String::from("hello");
let s2 = s1;
println! ("{}, world!", s1);
这他妈的是不是有点激进了, 直接 s1 就无效了, 也就是从 s1 move
到了 s2.
Rust 永远也不会对你的数据进行深拷贝. 因此运行时的这种拷贝几乎没有开销.
也可以深拷贝:
let s1 = String::from("hello");
let s2 = s1.clone();
好像有点李姐了所有权, 就是谁用谁负责.
赋值, 函数调用传参, 或者返回值都会转移所有权.
如果是函数调用传参的时候需要交出所有权, 函数调用完成了又要归还所有权, 但是如何实现函数调用的时候所有权还是在调用者那边呢?
References and Borrowing
引用和指针类似, 不同的是, 引用确保是指向了一个特定类型的有效的值.
let s1 = String::from("hello");
let len = calculate_length(&s1);
&s1
可以创建 s1
的引用, 但是并不拥有 s1, 所以引用不再使用的时候并不会调用 s1 的 drop
.
这样的用法被称之为 reference borrowing
.
举个例子, 就像现实生活中, 有一个人拥有一样东西, 你可以从他那儿借用, 但是你用完之后必须还给别人.
你借用的东西只能使用, 不能修改搞破坏.
就像变量默认是 immutable, 引用默认也是 immutable.
另外一种就是可修改的引用 (mutable reference).
fn main() {
let mut s = String::from("hello");
change(&mut s);
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
}
mutable reference 的使用有一个限制就是, 在同一段代码中, 只能有唯一的一个可变引用.
也就是, 我们的同一个东西不能借给别人两次.
我试了下 immutale reference 可以被借出去多次.
感觉和一般的编程语言不同的是, rust 给程序员施加了很多的限制, 不让你瞎几把写代码, 你只能按照我的规矩来写. C++和 C 就灵活许多, 但是这种灵活性未必不会对程序员带来负担. Java 就是在 C++ 的基础上做的减法.
感觉 rust 这种设计就是一种折中吧, 想要安全和内存管理的好处就要遵守我的规则, 算是在无 GC 和有 GC 开辟了第三条路吧, 挺好的, 我决定学学.
只能以 mutable reference 的方式借出一次的好处是 Rust 可以避免在编译期的数据竞争.
数据竞争 (data race) 和竞态条件类似:
- 同一时间有两个指针指向了同一片数据
- 至少有一个指针会修改数据
- 对于数据的访问没有任何同步机制
想要多个可变引用的方法就是, 我们可以通过大括号来创建多个域.
同样的, 不能在同一个域, 对同一个数据既有可变引用也同时有不可变引用.
但是如果先使用不可变引用, 后使用可变引用, 如果不重叠的话, 就没得问题.
编译器能告诉引用不再使用的位置的这种能力被称为 Non-Lexical-Lifetimes(NLL)
, 详情见这里.
尽管 borrowing error 可能会出现很多次, 但是这个是 Rust 在提示里这里可能会出现 bug.
悬挂指针 (Dangling References)
Rust 向您保证: 如果你引用了某个数据, 编译器将会确保数据不会先于引用脱离作用域滴.(好奇, 这个是如何保证的?)
这里搞个例子来演示编译器如何处理悬挂指针的:
fn main() {
let reference_to_nothing = dangle();
}
fn dangle() -> &String {
let s = String::from("hello");
&s
}
这里报错会提示到一个功能点: lifetimes
, 这个得等到第 10 章才讲.
由于 s
是在函数 dangle
中创建的, 所以在 dangle 结束之前, s 将会被 deallocated, 如果我们还返回这个值, 这意味着引用将会指向一个不存在的值, Rust 不允许我等这么干.
这个解决方法就是, 直接返回 String, 这样就会把所有权转移 (move) 出去了, 哇偶, 总之你调用我就转移给了你了, 下面的就是你负责了, 我就不管了. 所以调用函数还是得看函数的返回值.
引用的规则
- 在任何给定的时刻, 你要么只有一个可变的引用, 要么可以有一个或者多个不可变的引用
- 引用必须永远是有效的
下面会介绍另一个引用: slices
.
slice 类型
slice 可以让你以集合的方式引用连续序列的元素, 而不是整个集合本身. slice 是引用的一种, 所以它并不具有所有权.
文章评论