下面是上一节 Rust 速成课的练习解答。
这篇文章是基于 FP 完成 Rust 教学系列的一部分。 如果你在博客之外阅读这篇文章,你可以在介绍文章的顶部找到这个系列中所有文章的链接。 也可订阅 RSS 频道。
练习1
以下是已损坏的代码:
fn main() {
let val: String = String::from("Hello, World!");
printer(val);
printer(val);
}
fn printer(val: String) {
println!("The value is: {}", val);
}
我们收到了一条关于 move 的错误信息。 我们将在下一课讨论所有权时学习更多的 move。 有两种基本的解决方案。 首先,不太理想的一点是:
Clone
我们已经将原始 val 移动到第一个 printer() 调用中,并且不能再次使用它。 一种变通方法是将 val 的一个克隆移动到那个调用中,不影响原始的代码:
fn main() {
let val: String = String::from("Hello, World!");
printer(val.clone());
printer(val);
}
fn printer(val: String) {
println!("The value is: {}", val);
}
请注意,我只是在第一次克隆了 val,而不是第二次。 我们不需要在第二次调用之后再次使用 val,所以 move 它是安全的。 使用额外的克隆开销很大,因为它需要分配内存和执行缓冲区副本。
说起来很贵...
引用传递
与 move 值不同,我们可以通过引用将其传递给 printer() 函数。 让我们首先尝试通过修改 printer() 来实现这一点:
fn main() {
let val: String = String::from("Hello, World!");
printer(val);
printer(val);
}
fn printer(val: &String) {
println!("The value is: {}", val);
}
这不起作用,因为当我们调用 printer 时,我们仍然给它一个 String,而不是对 String 的引用。 解决这个问题非常简单:
fn main() {
let val: String = String::from("Hello, World!");
printer(&val);
printer(&val);
}
请注意,“&“ 符号的意思是:
还有一个更好的方法来写 printer:
fn printer(val: &str) {
println!("The value is: {}", val);
}
使用 str 而不是 String,我们可以传递字符串文本,而不需要强制分配堆对象。 我们将在讨论字符串时更详细地讨论这个问题。
练习2
以下是已损坏的代码:
fn main() {
let i = 1;
loop {
println!("i == {}", i);
if i >= 10 {
break;
} else {
i += 1;
}
}
}
我们从编译器得到的错误信息非常有用:
不能两次赋值给不可变变量 i
为了解决这个问题,我们将变量从不可变变为可变:
fn main() {
let mut i = 1;
...
练习3
这个练习问你什么时候可以省略分号,这里有一个简单的规则:
例如,在这段代码中,删除分号就可以了:
fn main() {
for i in 1..11 {
println!("i == {}", i);
}
}
也就是说,像这些纯粹有效的表达上留下分号似乎有些习惯用法。
练习4
这个练习是为了在 Rust 中实现 FizzBuzz,在这里重复以下规则:
如果数字是3和5的倍数,输出fizzbuzz而不是数字
这里有一个使用 if/else 回退的可能解决方案:
fn main() {
for i in 1..101 {
if i % 3 == 0 && i % 5 == 0 {
println!("fizzbuzz");
} else if i % 3 == 0 {
println!("fizz");
} else if i % 5 == 0 {
println!("buzz");
} else {
println!("{}", i);
}
}
}
这至少有一个缺点:它将可能需要多次测试 i%3 == 0 和 i%5 == 0。在表内部,编译器可以优化它。但是,重复的模数调用仍然只是嘲笑我们的代码!我们可以改为使用模式匹配:
fn main() {
for i in 1..101 {
match (i % 3 == 0, i % 5 == 0) {
(true, true) => println!("fizzbuzz"),
(true, false) => println!("fizz"),
(false, true) => println!("buzz"),
(false, false) => println!("{}", i),
}
}
}
或者,如果你想用通配符匹配来获得一些乐趣:
fn main() {
for i in 1..101 {
match (i % 3, i % 5) {
(0, 0) => println!("fizzbuzz"),
(0, _) => println!("fizz"),
(_, 0) => println!("buzz"),
(_, _) => println!("{}", i),
}
}
}
我不会告诉您哪个是 “最佳” 解决方案。当然,可以尝试其他实现。这是为了给您一些更多Rust结构的感觉。