100 Exercises To Learn Rust 挑战!构文・整数・变量

devtools/2024/9/22 14:46:20/

前一篇文章

【0】准备
【1】构文・整数・变量 ← 本次
全部文章列表
《100 Exercise To Learn Rust》第2回,也就是实际演习的第1回!从这次开始,我们会适度减少前置说明,直接进入问题的解决!

本次的相关页面

  • 1.1. Syntax
  • 2.1. Integers
  • 2.2. Variables

[01_intro/01_syntax] 基本文法

  • 1.1. Syntax

问题如下: 

rust">// TODO: fix the function signature below to make the tests pass.
//  Make sure to read the compiler error message—the Rust compiler is your pair programming
//  partner in this course and it'll often guide you in the right direction!
//
// The input parameters should have the same type of the return type.
fn compute(a, b) -> u32 {// Don't touch the function body.a + b * 2
}#[cfg(test)]
mod tests {use crate::compute;#[test]fn case() {assert_eq!(compute(1, 2), 5);}
}

“在Rust中,编译器是你最好的朋友,所以一定要认真听取编译器君的建议!” 大概是这个意思(非常意译)。实际上确实如此,所以这次我们首先来看一下编译错误。

rust">$ wrRunning tests...✅ (01) intro - (00) welcome (Skipped)❌ (01) intro - (01) syntaxMeditate on your approach and return. Mountains are merely mountains.error: expected one of `:`, `@`, or `|`, found `,`--> exercises/01_intro/01_syntax/src/lib.rs:6:13|
6 | fn compute(a, b) -> u32 {|             ^ expected one of `:`, `@`, or `|`|= note: anonymous parameters are removed in the 2018 edition (see RFC 1685)
help: if this is a `self` type, give it a parameter name|
6 | fn compute(self: a, b) -> u32 {|            +++++
help: if this is a parameter name, give it a type|
6 | fn compute(a: TypeName, b) -> u32 {|             ++++++++++
help: if this is a type, explicitly ignore the parameter name|
6 | fn compute(_: a, b) -> u32 {|            ++error: expected one of `:`, `@`, or `|`, found `)`--> exercises/01_intro/01_syntax/src/lib.rs:6:16|
6 | fn compute(a, b) -> u32 {|                ^ expected one of `:`, `@`, or `|`|= note: anonymous parameters are removed in the 2018 edition (see RFC 1685)
help: if this is a parameter name, give it a type|
6 | fn compute(a, b: TypeName) -> u32 {|                ++++++++++
help: if this is a type, explicitly ignore the parameter name|
6 | fn compute(a, _: b) -> u32 {|               ++error: could not compile `syntax` (lib) due to 2 previous errors
error: could not compile `syntax` (lib test) due to 2 previous errors

一开始可能会被长长的错误信息压倒,但这些错误并不是随便给出的,而是大多提供了便于查找的错误信息。所以如果认真阅读,能学到很多东西。

虽然显示了两个错误,但它们几乎是相同的。

rust">error: expected one of `:`, `@`, or `|`, found `,`--> exercises/01_intro/01_syntax/src/lib.rs:6:13|
6 | fn compute(a, b) -> u32 {|             ^ expected one of `:`, `@`, or `|`|= note: anonymous parameters are removed in the 2018 edition (see RFC 1685)

由于是语法问题,因此出现了与语法相关的错误。就像在做“理所当然体操”一样。

在错误信息下面有三条帮助提示,它们似乎根据我们的意图进行了分类。

  • 如果是 self 类型,请加上 self
  • self 是定义方法时的语法糖,这次不适用。
  • 如果是指参数(参数名),请写上类型。
  • 如果是指类型名,请加上参数名。 (虽然并没有这样直接写,而是提示使用 _ 来表示未使用的参数。)

这次的错误可能与第二点或第三点有关,即“应该同时明确写出参数名和类型,但只写了其中之一”。

在绑定变量时可以省略类型名(后面会详细说明,但必须静态确定),不过由于Rust是静态类型语言,所以在函数签名中特别需要明确指定类型。

解说

我们回到问题所在的部分。ab 是以小写字母开头的标识符。在Rust中,变量名通常采用蛇形命名法(snake_case),而类型名和特征名则采用帕斯卡命名法(PascalCase),因此这些标识符应该是变量名。

因此,我们需要为它们添加类型名。由于Rust不会进行隐式类型转换,为了最小化修改(也就是遵循“不要触碰函数体”的原则),将它们设为 u32 类型似乎是最合适的选择。

rust">- fn compute(a, b) -> u32 {+ fn compute(a: u32, b: u32) -> u32 {// Don't touch the function body.a + b * 2
}

成功通过了!让我们继续下一个问题。

(按顺序的话应该是 02_basic_calculator/00_intro,不过看起来只是做个标题调用,所以我们跳过它。后面的章节似乎也是类似的情况。)

[02_basic_calculator/01_integers] 整数型

  • 2.1. Integers

问题如下: 

rust">fn compute(a: u32, b: u32) -> u32 {// TODO: change the line below to fix the compiler error and make the tests pass.a + b * 4u8
}#[cfg(test)]
mod tests {use crate::compute;#[test]fn case() {assert_eq!(compute(1, 2), 9);}
}

虽然与刚才的问题类似,但这次更强调了 没有隐式类型转换 这一点。

解说

4u8 是一个表示 u8 类型的数字 4 的字面量。由于不会进行隐式类型转换,如果一开始就将其设定为 u32 类型的 4,效果会更好。

rust">fn compute(a: u32, b: u32) -> u32 {// TODO: change the line below to fix the compiler error and make the tests pass.-    a + b * 4u8+    a + b * 4u32}

u32u8 中的 u 代表无符号整数(unsigned integer),i32i8 中的 i 代表整数(integer)。在Rust中,为了防止出现错误,通常在不需要表示负数时会使用无符号整数。

虽然有很多关于二进制补码和字节宽度的讨论,但最终我们会关心“最大值和最小值到底是多少?” 这些值可以通过 MINMAX 方法来确认。

  • i32::MIN ( = -2147483648 )
  • u32::MAX ( = 4294967295 )

此外,虽然在本节中尚未提及,但 usize 可能是你最常见到的整数类型。之所以如此,是因为这个整数类型非常特殊,它可以用于数组的索引。换句话说,usize 是一种接近地址值的类型。在32位操作系统中,它是32位宽;在64位操作系统中,它是64位宽,因此被赋予了“size”这个名称。

[02_basic_calculator/02_variables] 变量

  • 2.2. Variables

问题如下: 

rust">pub fn speed(start: u32, end: u32, time_elapsed: u32) -> u32 {// TODO: define a variable named `distance` with the right value to get tests to pass//  Do you need to annotate the type of `distance`? Why or why not?// Don't change the line belowdistance / time_elapsed
}#[cfg(test)]
mod tests {use crate::speed;#[test]fn case1() {assert_eq!(speed(0, 10, 10), 1);}#[test]fn case2() {assert_eq!(speed(10, 30, 10), 2);}#[test]fn case3() {assert_eq!(speed(10, 31, 10), 2);}
}

提示内容是:distance 变量尚未定义,请定义它。

解说

通过用终点坐标减去起点坐标可以得到距离,因此可以将其绑定为 distance 变量。

rust">pub fn speed(start: u32, end: u32, time_elapsed: u32) -> u32 {// TODO: define a variable named `distance` with the right value to get tests to pass//  Do you need to annotate the type of `distance`? Why or why not?+    let distance = end - start;// Don't change the line belowdistance / time_elapsed
}

从练习的意图来看,似乎是想引导你思考:“既然是强类型静态语言,为什么不需要为 distance 指定类型(比如写作 let distance: u32 = ...)呢?” 其实,Rust具有相当强大的类型推断机制,如果可以从相关的运算等推断出类型,就不会出现错误。

另外,VSCode 的 rust-analyzer插件还具有一个功能,即“当类型可以被推断时,会以灰色显示该类型”。

“Rust一开始有点难上手,但自从 rust-analyzer 能帮忙添加类型注释后,就变得容易多了,” 这是我朋友说的。而实际上,这个功能确实使得Rust既易于编写又保持对类型的严格要求,同时也让 VSCode + rust-analyzer 成为了最好的Rust编辑器组合。

总结

最后一个问题很有特点,通过这三道题我们了解到:

  • 函数的签名必须由人手动指定。
  • 作为一种强类型静态语言,显然不允许隐式类型转换。
  • 由于类型推断机制,变量的类型注释在一定程度上可以省略。

这表明,Rust并不是要求在所有地方都必须进行类型注释,而是在需要的地方,比如函数签名,才需要人手动进行类型注释。正因为这一特点,笔者认为,Rust的类型系统相比其他语言更容易上手。

下一篇文章: 【2】if・panic・练习


http://www.ppmy.cn/devtools/92594.html

相关文章

单例模式(懒汉式、饿汉式)

一、普通情况 正常在实体类下定义一个属性,在测试类中去创建他两次,此时stu1和stu2的内存地址不同 public class Student {private String name; } 二、单例模式下 (一) 懒汉式 在实体类下设计私有的构造方法,导致在测试类中无法new一个…

四数相加2 | LeetCode-454 | 哈希集合 | Java详细注释

🙋大家好!我是毛毛张! 🌈个人首页: 神马都会亿点点的毛毛张 🕹️思路:四数相加 > 两数相加 📌LeetCode链接:454. 四数相加 II 文章目录 1.题目描述🍎2.题解&#x…

工厂模式和策略模式的核心区别与最佳实践

工厂模式和策略模式的核心区别与最佳实践 在软件设计中,设计模式是解决特定问题的常用方法。工厂模式(Factory Pattern)和策略模式(Strategy Pattern)是两种非常重要的设计模式,广泛应用于Java开发中。尽管…

spingboot mongoDB实现文件的上传、下载、预览

pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0…

开源应用:AI监测如何成为社会安全的智能盾牌

社会背景 随着社会的快速发展&#xff0c;社会安全管理正站在一个新时代的门槛上。社会对安全管理的需求不断增长&#xff0c;传统的安全措施已难以满足现代社会的需求。AI技术以其独特的数据处理和模式识别能力&#xff0c;正在成为我们社会安全的智能盾牌。 AI大模型识别功能…

计算机毕业设计选题推荐-电缆行业生产管理系统-Java/Python项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

Codeforces Round 961 【C. Squaring】

C. Squaring 题目大意&#xff1a; 给你一个长度为n的数组&#xff0c;求最少次操作&#xff0c;使得数组&#xff08;非严格&#xff09;递增。一次操作&#xff1a;Ai 变为 Ai^2。 不可能实现输出-1。 关键思路&#xff1a; 分子分母同时取对数&#xff0c;比值不变。 …

代码随想录算法训练营 | 贪心算法 part01

贪心的本质是选择每一阶段的局部最优&#xff0c;从而达到全局最优。 什么是局部最优&#xff0c;局部最优能否推出全局最优&#xff08;举反例 举不出来的时候就可以试试贪心&#xff09; 455.分发饼干 455.分发饼干 局部最优&#xff1a;大饼干就要优先分给胃口大的孩子&am…