练习题来自:https://practice-zh.course.rs/flow-control.html
1
rust
// 填空
fn main() {
let n = 5;
if n < 0 {
println!("{} is negative", n);
} __ n > 0 {
println!("{} is positive", n);
} __ {
println!("{} is zero", n);
}
}
非常简单的if-else
rust
fn main() {
let n = 5;
if n < 0 {
println!("{} is negative", n);
} else if n > 0 {
println!("{} is positive", n);
} else {
println!("{} is zero", n);
}
}
2 🌟🌟 if/else 可以用作表达式来进行赋值
rust
// 修复错误
fn main() {
let n = 5;
let big_n =
if n < 10 && n > -10 {
println!(" 数字太小,先增加 10 倍再说");
10 * n
} else {
println!("数字太大,我们得让它减半");
n / 2.0 ;
}
println!("{} -> {}", n, big_n);
}
能通过if
表达式直接赋值也是现代语言的一个新鲜玩意了,不知道为什么C++不打个补丁支持下。
rust
fn main() {
let n = 5;
let big_n = if n < 10 && n > -10 {
println!(" 数字太小,先增加 10 倍再说");
10 * n
} else {
println!("数字太大,我们得让它减半");
n / 2
};
println!("{} -> {}", n, big_n);
}
3 for in 可以用于迭代一个迭代器,例如序列 a...b.
rust
fn main() {
for n in 1..=100 { // 修改此行,让代码工作
if n == 100 {
panic!("NEVER LET THIS RUN")
}
}
}
始终记得Rust的range是一个左闭右开区间,想要包含右边就得加上等号就行,这里就要去掉。
rust
fn main() {
for n in 1..100 { // 修改此行,让代码工作
if n == 100 {
panic!("NEVER LET THIS RUN")
}
}
}
题外话:Rust 目前还是一个比较新的语言,主流的 IDE 为 VS Code(安装 rust-analyzer 插件)和 RustRover,前者无论商用还是非商用都是不收费的,后者非商用不收费,商用收费(应该是 Jetbrains 旗下第一个这么收费的,其他 IDE 都是任何使用都得收费)。下面提到的 VS Code 都指的是加载了 rust-analyzer 插件的。
就我个人的体验来看,两者各有各的长处。
这是 VS Code 的代码:
这是 Rustrover 的代码:
可以看到 VS Code 对变量的染色更充分一点,不过 Rustrover 对这个 range
的提示就很不错,你基本不可能认为会跑到 100。由于Rust存在类型推导的机制,因此当你不清楚变量的类型时,IDE提供的类型提示就尤为重要。这点 Rustrover 不如 VS Code,尤其是对于一些特征的推导,Rustrover 只能推导出基本类型,VS Code就能推导出impl trait
。
另外如果你想阅读库代码时,Rustrover有个将注释文档排版的功能,非常不错:
VS Code这边可能是考虑到你用浏览器也能访问本地排版好的文档,就没有这个功能,少开一个浏览器还是省了不少事的。
我们接下来的代码都以 VS Code 为基准 IDE。
4
rust
// 修复错误,不要新增或删除代码行
fn main() {
let names = [String::from("liming"),String::from("hanmeimei")];
for name in names {
// do something with name...
}
println!("{:?}", names);
let numbers = [1, 2, 3];
// numbers中的元素实现了 Copy,因此无需转移所有权
for n in numbers {
// do something with name...
}
println!("{:?}", numbers);
}
string
不是基本类型,因此需要借用
rust
fn main() {
let names = [String::from("liming"),String::from("hanmeimei")];
for name in &names {
// do something with name...
}
println!("{:?}", names);
let numbers = [1, 2, 3];
// numbers中的元素实现了 Copy,因此无需转移所有权
for n in numbers {
// do something with name...
}
println!("{:?}", numbers);
}
5
rust
fn main() {
let a = [4,3,2,1];
// 通过索引和值的方式迭代数组 `a`
for (i,v) in a.__ {
println!("第{}个元素是{}",i+1,v);
}
}
固定用法了。
rust
fn main() {
let a = [4,3,2,1];
// 通过索引和值的方式迭代数组 `a`
for (i,v) in a.iter().enumerate() {
println!("第{}个元素是{}",i+1,v);
}
}
6 🌟🌟 当条件为 true 时,while 将一直循环
rust
// 填空,让最后一行的 println! 工作 !
fn main() {
// 一个计数值
let mut n = 1;
// 当条件为真时,不停的循环
while n __ 10 {
if n % 15 == 0 {
println!("fizzbuzz");
} else if n % 3 == 0 {
println!("fizz");
} else if n % 5 == 0 {
println!("buzz");
} else {
println!("{}", n);
}
__;
}
println!("n 的值是 {}, 循环结束",n);
}
这道题应该是拿大名鼎鼎的 Fizzbuzz
题目改的。
rust
fn main() {
// 一个计数值
let mut n = 1;
// 当条件为真时,不停的循环
while n < 10 {
if n % 15 == 0 {
println!("fizzbuzz");
} else if n % 3 == 0 {
println!("fizz");
} else if n % 5 == 0 {
println!("buzz");
} else {
println!("{}", n);
}
n += 1;
}
println!("n 的值是 {}, 循环结束", n);
}
我就顺便把这道题做了吧,应该是我用Rust写的第一道算法题,之前用C++写过一遍。Fizzbuzz题目如下:
给你一个整数 n ,找出从 1 到 n 各个整数的 Fizz Buzz 表示,并用字符串数组 answer(下标从 1 开始)返回结果,其中:
answer[i] == "FizzBuzz" 如果 i 同时是 3 和 5 的倍数。
answer[i] == "Fizz" 如果 i 是 3 的倍数。
answer[i] == "Buzz" 如果 i 是 5 的倍数。
answer[i] == i (以字符串形式)如果上述条件全不满足。
这里用 LeetCode 的提交格式:
rust
impl Solution {
pub fn fizz_buzz(n: i32) -> Vec<String> {
let mut i = 1;
let mut res: Vec<String> = Vec::new();
while i <= n {
if i % 15 == 0 {
res.push("FizzBuzz".to_string());
} else if i % 3 == 0 {
res.push("Fizz".to_string());
} else if i % 5 == 0 {
res.push("Buzz".to_string());
} else {
res.push(i.to_string());
}
i += 1;
}
res
}
}
7 🌟 使用 break 可以跳出循环
rust
// 填空,不要修改其它代码
fn main() {
let mut n = 0;
for i in 0..=100 {
if n == 66 {
__
}
n += 1;
}
assert_eq!(n, 66);
}
答案
rust
fn main() {
let mut n = 0;
for _ in 0..=100 {
if n == 66 {
break;
}
n += 1;
}
assert_eq!(n, 66);
}
8 🌟 continue 会结束当次循环并立即开始下一次循环
rust
// 填空,不要修改其它代码
fn main() {
let mut n = 0;
for i in 0..=100 {
if n != 66 {
n+=1;
__;
}
__
}
assert_eq!(n, 66);
}
答案
rust
// 填空,不要修改其它代码
fn main() {
let mut n = 0;
for i in 0..=100 {
if n != 66 {
n+=1;
continue;
}
break;
}
assert_eq!(n, 66);
}
9 🌟🌟 loop 一般都需要配合 break 或 continue 一起使用。
rust
// 填空,不要修改其它代码
fn main() {
let mut count = 0u32;
println!("Let's count until infinity!");
// 无限循环
loop {
count += 1;
if count == 3 {
println!("three");
// 跳过当此循环的剩余代码
__;
}
println!("{}", count);
if count == 5 {
println!("OK, that's enough");
__;
}
}
assert_eq!(count, 5);
}
答案
rust
fn main() {
let mut count = 0u32;
println!("Let's count until infinity!");
// 无限循环
loop {
count += 1;
if count == 3 {
println!("three");
// 跳过当此循环的剩余代码
continue;;
}
println!("{}", count);
if count == 5 {
println!("OK, that's enough");
break;
}
}
assert_eq!(count, 5);
}
C++是没有loop
(无限循环)的,很多其他的语言也没有,原因很明显,无限循环一旦不写终止条件,很容易就成了死循环。而Rust的编译器也没有智能到检测到死循环的存在,比如这里注释掉第二个if
的break
,loop
直接就死循环了,编译器也是很老实地跑,没有任何报错或者警告。倒是VS Code给了一个代码不可达(unreachable)的提示。
所以我不是很建议在代码里使用loop
。
10 🌟🌟 loop 是一个表达式,因此我们可以配合 break 来返回一个值
rust
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
__;
}
};
assert_eq!(result, 20);
}
很有意思的设计,C++实现类似功能估计要多写点代码------包个函数就有入栈出栈的性能损耗,直接修改又不容易让人意识到变量已经被修改了。
rust
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter;
}
};
assert_eq!(result, 20);
}
11 🌟🌟🌟 当有多层循环时,你可以使用 continue 或 break 来控制外层的循环。要实现这一点,外部的循环必须拥有一个标签 'label, 然后在 break 或 continue 时指定该标签
rust
// 填空
fn main() {
let mut count = 0;
'outer: loop {
'inner1: loop {
if count >= 20 {
// 这只会跳出 inner1 循环
break 'inner1; // 这里使用 `break` 也是一样的
}
count += 2;
}
count += 5;
'inner2: loop {
if count >= 30 {
break 'outer;
}
continue 'outer;
}
}
assert!(count == __)
}
答案
rust
// 填空
fn main() {
let mut count = 0;
'outer: loop {
'inner1: loop {
if count >= 20 {
// 这只会跳出 inner1 循环
break 'inner1; // 这里使用 `break` 也是一样的
}
count += 2;
}
count += 5;
'inner2: loop {
if count >= 30 {
break 'outer;
}
continue 'outer;
}
}
assert!(count == 30)
}
对比其他语言的goto
,Rust的这个弱化了一点,仅仅能对循环打label
,但是这已经让人不好走查代码了。我的建议也是:不要用。几乎所有编程书在介绍语言里的goto
时都不建议使用,用起来很爽,看代码很痛苦。