用 rustlings 练习 Rust 语言
// This is a quiz for the following sections:
// - Variables
// - Functions
// - If
//
exercises/variables/variables1.rs
Variables
In Rust, variables are immutable(不可变的) by default.
When a variable is immutable, once a value is bound to a name, you can’t change that value.
You can make them mutable by adding mut
in front of the variable name.
Further information
- Variables and Mutability
exercises/variables/variables2.rs
- 变量
- 函数
exercises/variables/variables3.rs
- 错误
--> exercises/variables/variables3.rs:9:27
|
8 | let x: i32;
| - binding declared here but left uninitialized
9 | println!("Number {}", x);
| ^ `x` used here but it isn't initial
在 Rust 中,这段代码存在一个主要问题:变量 x 被声明了但没有初始化。
Rust 要求所有变量在使用前必须被初始化,即使是显式声明了类型但没有赋值的变量
c++ 没有初始化 不会报错,但是 默认不一定是0 可能是随机值。 因此 自己写代码 默认一个值
代码语言:javascript代码运行次数:0运行复制void exampleFunction() {
int localVar; // localVar 是未初始化的
std::cout << localVar; // 未定义行为,可能导致程序崩溃或不正确的输出
}
- 修改
// variables3.rs
//
// Execute `rustlings hint variables3` or use the `hint` watch subcommand for a
// hint.
fn main() {
let x: i32 = 1;
// 在c++中 默认值 int a; 可能是 0 可能是其他很大值 但是不会编译报错
println!("Number {}", x);
}
exercises/variables/variables4.rs:
- what
| let x = 3;
| -
| |
| first assignment to `x`
| help: consider making this binding mutable: `mut x`
10 | println!("Number {}", x);
11 | x = 5; // don't change this line
| ^^^^^ cannot assign twice to immutable variable
- why
在 Rust 中,这段代码会引发一个编译错误。原因在于 Rust 的变量默认是不可变的(immutable),这意味着一旦声明并赋予初始值后,你不能再次修改它,除非你明确地将其声明为可变的。
以下是你的代码中的问题:
- 变量
x
被声明并初始化为3
。 - 尝试将
x
重新赋值为5
,但由于x
没有被声明为可变的,这会导致编译错误。
要修复这个问题,你需要使用 mut
关键字将 x
声明为可变变量,如下所示:
fn main() {
let mut x = 3; // 使用 mut 使 x 成为可变变量
println!("Number {}", x);
x = 5; // 现在可以给 x 重新赋值
println!("Number {}", x);
}
这段代码现在将正确编译并运行,首先打印 x
的初始值 3
,然后将其值更改为 5
并打印新的值。
exercises/variables/variables5.rs
在 Rust 中,这段代码存在几个问题:
- 变量
number
被声明为一个字符串"T-H-R-E-E"
,这意味着它的类型是&str
(字符串切片)。 - 尝试将
number
重新赋值为一个整数3
,这是不允许的,因为number
已经被声明为字符串类型,不能直接赋值为整数。 - 即使
number
是一个整数,number + 2
这样的表达式也是不允许的,因为 Rust 是一种静态类型语言,它不允许隐式类型转换。你需要显式地转换类型。
要修复这些问题,你可以创建一个新的变量来存储整数,然后将其与 2 相加。例如:
代码语言:javascript代码运行次数:0运行复制fn main() {
let number = "T-H-R-E-E"; // 保持不变
println!("Spell a Number : {}", number);
let number = 3; // 创建一个新的变量来存储整数
println!("Number plus two is : {}", number + 2);
}
但请注意,在 Rust 中,你不能有相同作用域内的两个变量使用相同的名称。如果你想要同时使用字符串和整数,你需要使用不同的变量名,或者使用更复杂的数据结构(如元组或结构体)来存储不同类型的值。
例如,如果你想要在同一个作用域内使用字符串和整数,你可以使用元组:
代码语言:javascript代码运行次数:0运行复制fn main() {
let number_str = "T-H-R-E-E";
println!("Spell a Number : {}", number_str);
let number_tuple: (i32, i32) = (3, 2); // 使用元组来存储两个整数
println!("Number plus two is : {}", number_tuple.0 + number_tuple.1);
}
在这个例子中,我们使用元组 (number_tuple.0, number_tuple.1)
来存储整数,并使用 .0
和 .1
来访问元组中的值。然后,我们打印出 number_tuple.0
加上 number_tuple.1
的结果。
在 Rust 中,这段代码看起来是正确的,但是根据注释提示,可能存在一些不明显的问题。注释中提到的 "Data Types" 提示我们可能需要考虑类型的问题。
问题在于 println!
宏的占位符和传递给它的参数类型不匹配。println!
宏中的占位符 "Number {}"
期望一个可以被格式化为 Display
的类型,但是 NUMBER
被声明为一个 const
整数,其类型是 i32
。整数类型不能直接被格式化为字符串,因此你需要使用 {}
占位符来打印整数。
以下是修正后的代码:
代码语言:javascript代码运行次数:0运行复制// Data Types
const NUMBER: i32 = 3; // 明确 NUMBER 的类型为 i32
fn main() {
println!("Number {}", NUMBER); // 使用 {} 占位符正确地打印整数
}
此外,虽然在 Rust 中 const
的类型可以被推断出来,但是显式地声明类型是一个好习惯,特别是在示例代码或教学材料中,因为它可以提高代码的可读性。
这段代码现在将正确编译并运行,打印出 "Number 3"。如果 NUMBER
需要被用作字符串,你需要将其声明为一个字符串字面量,例如:
const NUMBER: &str = "3";
fn main() {
println!("Number {}", NUMBER); // 现在 NUMBER 是一个字符串,可以正确地格式化
}
这样 NUMBER
就是一个字符串切片,可以被 println!
宏中的 {}
占位符正确地格式化和打印。
Functions
Here, you'll learn how to write functions and how the Rust compiler can help you debug errors even in more complex code.
Further information
- How Functions Work
exercises/functions/functions1.rs
代码语言:javascript代码运行次数:0运行复制error[E0425]: cannot find function `call_me` in this scope
--> exercises/functions/functions1.rs:9:5
|
9 | call_me();
| ^^^^^^^ not found in this scope
在 Rust 中,这段代码存在一个问题:call_me
函数在 main
函数中被调用,但是没有定义。为了使这段代码能够正确编译和运行,你需要定义一个名为 call_me
的函数,它可以是任何形式的函数,例如一个简单的打印函数。
下面是如何定义 call_me
函数的一个示例:
fn call_me() {
println!("Called!");
}
fn main() {
call_me();
}
在这个示例中,call_me
是一个没有参数也没有返回值的函数,它使用 println!
宏打印 "Called!" 到控制台。然后,在 main
函数中调用了 call_me
函数。
如果你想要 call_me
函数具有一些功能,你可以添加参数和/或返回值。例如:
fn call_me() -> String {
String::from("Called!")
}
fn main() {
let message = call_me();
println!("{}", message);
}
在这个例子中,call_me
函数返回一个 String
类型的值 "Called!"。然后在 main
函数中,我们调用 call_me
并将其返回值赋给变量 message
,然后打印这个变量。
确保你的 call_me
函数定义在 main
函数之前或在同一个作用域内,以便 main
函数能够调用它。
在 Rust 编程语言中,fn
是用来声明一个函数的关键字。当你想要创建一个新的函数时,你需要使用 fn
来开始函数的定义,后面跟着函数的名称、参数列表(如果有的话),以及函数体。
函数的基本语法如下:
代码语言:javascript代码运行次数:0运行复制fn function_name(parameters) -> return_type {
// 函数体
}
function_name
是你为函数指定的名称。parameters
是函数的参数列表,参数之间用逗号分隔。如果函数不需要参数,可以省略这个部分。return_type
是函数返回值的类型。如果函数不返回任何值,可以使用()
表示返回空元组。- 函数体是大括号
{}
内的代码,这里是函数执行的操作。
下面是一些函数的示例:
- 一个没有参数也不返回任何值的函数:
fn say_hello() {
println!("Hello!");
}
- 一个带有一个参数的函数,不返回任何值:
fn print_number(number: i32) {
println!("The number is: {}", number);
}
- 一个没有参数但返回一个
i32
类型值的函数:
fn add_two_numbers(a: i32, b: i32) -> i32 {
a + b
}
- 一个带有两个参数并返回一个
String
类型值的函数:
fn greet(name: &str) -> String {
format!("Hello, {}!", name)
}
在这些示例中,fn
关键字用于定义函数的开始,函数名后面跟着参数列表和返回类型(如果有的话),然后是函数体,它包含了函数执行的具体操作。
functions2.rs
functions3.rs
这段 Rust 代码中存在几个问题:
- 函数调用与定义不匹配:在
main
函数中,call_me()
被调用时没有传递任何参数,但是在call_me
函数的定义中,它期望一个类型为u32
的参数num
。 - 类型注解错误:在
call_me
函数的定义中,参数列表里的num:
后面缺少了参数名。正确的语法应该是fn call_me(num: u32)
。 - 逻辑问题:
for
循环中的i + 1
会在打印时导致从 1 开始计数,这是正确的,但如果num
是 0,这将导致无限循环,因为0..num
的范围是空的,没有元素可以迭代。
修正这些问题后的代码如下:
代码语言:javascript代码运行次数:0运行复制fn main() {
call_me(5); // 传递一个 u32 类型的参数给 call_me 函数
}
fn call_me(num: u32) { // 正确地声明了参数名和类型
for i in 1..num { // 从 1 开始迭代,以避免当 num 为 0 时的无限循环
println!("Ring! Call number {}", i);
}
}
在这个修正后的版本中:
main
函数现在传递了一个u32
类型的参数5
给call_me
函数。call_me
函数正确地声明了参数num
并移除了错误的冒号。for
循环现在从 1 开始迭代到num - 1
,这避免了当num
为 0 时的潜在无限循环问题,并且打印的 "Ring! Call number" 将从 1 开始计数。
在 Rust 语言中,函数的返回值可以显式地使用 return
关键字来返回,也可以通过函数签名末尾的类型来隐式地返回值。这里有两种情况:
显式返回:使用 return
关键字来返回一个值。这是大多数编程语言中常见的做法。
fn sum(a: i32, b: i32) -> i32 {
return a + b;
}
隐式返回:在 Rust 中,如果函数的最后一个表达式是一个没有被分号 ;
终止的表达式,那么这个表达式的值将被用作函数的返回值。这种方式可以使得代码更加简洁。
fn sum(a: i32, b: i32) -> i32 {
a + b
}
在上面的第二个例子中,由于 a + b
是函数中的最后一个表达式,并且没有使用分号,所以它的值将被隐式地返回。
需要注意的是,如果函数的返回类型是 ()
(即单元类型),则不需要返回任何值,无论是显式还是隐式。
fn do_nothing() {
// 这个函数没有返回值,因为其返回类型是 ()
}
这种设计使得 Rust 代码在某些情况下更加简洁,同时也保持了类型安全和表达式意图的清晰。
If
if
, the most basic (but still surprisingly versatile!) type of control flow, is what you'll learn here.
Further information
- Control Flow - if expressions
f2.rs
代码语言:javascript代码运行次数:0运行复制 --> exercises/if/if2.rs:14:9
|
10 | pub fn foo_if_fizz(fizzish: &str) -> &str {
| ---- expected `&str` because of return type
...
14 | 1
| ^ expected `&str`, found integer
在您的 if2.rs
代码中,foo_if_fizz
函数试图根据传入的字符串返回不同的结果。但是,存在几个问题需要解决:
- 函数的返回类型是
&str
,但是else
分支返回了一个整型1
,这会导致类型不匹配错误。 - 需要实现
else
分支返回的字符串,以满足测试bar_for_fuzz
和default_to_baz
的要求。
下面是修复后的代码:
代码语言:javascript代码运行次数:0运行复制pub fn foo_if_fizz(fizzish: &str) -> &str {
if fizzish == "fizz" {
"foo"
} else if fizzish == "fuzz" {
"bar"
} else {
"baz"
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn foo_for_fizz() {
assert_eq!(foo_if_fizz("fizz"), "foo");
}
#[test]
fn bar_for_fuzz() {
assert_eq!(foo_if_fizz("fuzz"), "bar");
}
#[test]
fn default_to_baz() {
assert_eq!(foo_if_fizz("literally anything"), "baz");
}
}
在这个修复版本中,我添加了一个新的 else if
分支来处理当 fizzish
是 "fuzz"
时返回 "bar"
。同时,对于所有其他情况,使用 else
分支返回 "baz"
。这样,所有的测试都应该能够通过。
确保您的测试函数中的断言调用是正确的,并且测试用例的名称与测试内容相匹配。在您的原始代码中,测试用例 bar_for_fuzz
和 default_to_baz
的名称可能会让人有些困惑,因为它们似乎暗示了不同的行为,但实际上它们都期望返回 "bar"
和 "baz"
。在修复后的代码中,我已经假设测试用例的名称是正确的,并且它们分别测试了当输入为 "fuzz"
和任何其他字符串时的行为。
/**
【昵称】小王同学
【坐标】山东
【自我介绍】
1. 高级后端工程师,擅长c++并发编程。
2. 专注分布式存储,分布式数据库。
3. 时间的践行者,立志成为一名讲师。
【我的成绩】
1. 为了走出农村,2次高考
一次考研失败,
遇挫之后不知道干什么,开启被动之旅。
2. 为了找到合适工作,
深入研究c++书籍和leetcode 200题目
3. 为了提高项目能力,参与开源项目建设。
4. 为了把简单事情说清楚/要干啥
按照《只管去做》,《福格行为模型>>方法。
纸上得来终觉浅,绝知此事要躬行
做一个践行者。
【我能提供】
1. 后端程序员的简历优化+就业辅导+职业规划
2. 全栈工程师(c++,rust,go,python )项目开发
3. 一年践行12本书践行记录。
【希望一起解决什么,开启破圈之旅】
1. 交接更多朋友,抱团取暖。
寻找意义本身就更加有意义。
2. 无法做整个系统,聚焦一个模块
道可道也,非恒道也
名可名也,非恒名也。
无名 万物之始也
有名 万物之母也
别想太多,只管去做,躬身入局
链接我: # 微信(github):watchpoints
#公众号:后端开发成长指南
**/
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。原始发表:2024-07-06,如有侵权请联系 cloudcommunity@tencent 删除rust变量编译函数字符串