熟悉 Rust 的语法
所以,您决定学习 Rust。
不错的选择!Rust 是一门很棒的语言,它结合了系统编程的强大功能和现代语言特性,可以用于 Web 开发和区块链。
然而,在学习 Rust 时,其中一个障碍就是熟悉它的语法。
在本文中,我将尽力提供一些让您感到舒服的示例。
入门:变量和类型
让我们从基础开始:变量。
默认情况下,Rust 变量是不可变的。如果你习惯使用 Python 或 JavaScript 等允许变量更改的语言,这可能听起来很奇怪。
fn main() {
let x = 5; // x is immutable by default
// x = 6; // Uncommenting this will throw a compiler error
let mut y = 5; // y is mutable
y = 6; // No problem here
}
注意到let
关键字了吗?这就是 Rust 中声明变量的方式。如果要更改变量,请使用 mut 关键字使其可变。
类型注解
Rust 具有很好的类型推断:编译器通常知道变量的类型。
但有时,您需要自己指定类型:
// Here, we're explicitly saying that z is a 32-bit integer
let z: i32 = 10;
Rust 的类型系统是其一大优势,因此值得尽早熟悉它。
功能
如果你使用过其他语言,Rust 中的函数看起来应该很熟悉。但是有一些语法怪癖需要注意。
fn add(a: i32, b: i32) -> i32 {
a + b // No semicolon means this is the return value
}
注意,我们使用 -> 来定义函数的返回类型。另外,这里没有 return 关键字;如果省略分号,Rust 默认返回最后一个表达式。
一旦习惯了就会感觉很好。
所有权和借款
好吧,事情开始变得有趣了。Rust 的所有权模型让它脱颖而出,但一开始可能会有点棘手。
让我们看另一个例子
所有权
在 Rust 中,每个值都有一个变量,它是它的所有者。
当所有者超出范围时,该值将被丢弃。这就是 Rust 避免内存泄漏的方法。
fn main() {
let s1 = String::from("hello");
let s2 = s1; // Ownership of the String is moved to s2, s1 is now invalid
// println!("{}", s1); // This would cause a compile-time error
}
这里,当字符串移动到 s2 之后,s1 不再拥有该字符串。
如果你之后尝试使用 s1,Rust 将不会让你这么做。这就像 Rust 在说:“嘿,这已经不是你的了。”
借款
但是如果你想使用一个值而不拥有它的所有权该怎么办呢?
这就是借用的地方。您可以通过使用引用来借用一个值:
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1); // We're borrowing s1 here
println!("The length of '{}' is {}.", s1, len);
}
fn calculate_length(s: &String) -> usize {
s.len()
}
在这个例子中,&s1 是reference
s1 的 a 。calculate_length 函数暂时借用了 s1 ,但没有取得所有权。函数执行完毕后,s1 仍然有效。这很酷。
生命周期
生命周期是 Rust 跟踪引用有效时间的方式。
它们最初可能会令人困惑,但它们对于安全的内存管理至关重要。
让我们看一个非常基本的例子来熟悉它。
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
这里, 'a 是一个生命周期参数。这意味着引用 x 和 y 必须至少与返回值一样长。这确保我们不会返回对已经被丢弃的对象(或对象)的引用。
模式匹配
Rust 的 match 语句就像是兴奋剂一样。它是我最喜欢 Rust 的部分之一,因为它功能强大,表达力极强。
fn main() {
let number = 7;
match number {
1 => println!("One!"),
2 => println!("Two!"),
3 | 4 | 5 => println!("Three, Four, or Five!"),
6..=10 => println!("Between Six and Ten!"),
_ => println!("Anything else!"),
}
}
match 语句会根据多个模式检查一个值,并运行第一个匹配模式的代码。_ 是一个包罗万象的模式,当你需要处理任何未明确匹配的内容时非常有用。
使用模式匹配进行解构
您还可以使用它match
来解构元组或枚举等复杂数据类型。
fn main() {
let pair = (2, 5);
match pair {
(0, y) => println!("First is zero and y is {}", y),
(x, 0) => println!("x is {} and second is zero", x),
_ => println!("No zeroes here!"),
}
}
这只是皮毛而已。
Match 可以做更多的事情,但这应该能为你打下坚实的基础。
错误处理
Rust 没有异常。相反,它使用 Result 和 Option 类型来处理错误。乍一看可能有点冗长,但它比未检查异常安全得多。
fn main() {
let result = divide(10, 2);
match result {
Ok(v) => println!("Result is {}", v),
Err(e) => println!("Error: {}", e),
}
}
fn divide(a: i32, b: i32) -> Result<i32, String> {
if b == 0 {
Err(String::from("Division by zero"))
} else {
Ok(a / b)
}
}
这里的Result
类型可以是 Ok(成功)或 Err(错误)。这迫使你处理成功和失败的情况,这对于编写健壮的代码非常有用。
?
操作员
为了使错误处理更加人性化,Rust 提供了?
Operator。它是传播错误的简写。
fn main() -> Result<(), String> {
let result = divide(10, 0)?; // If divide returns Err, it returns from the function immediately
println!("Result is {}", result);
Ok(())
}
这就是 Rust 的说法,“如果出现错误,就返回它。”
高级语法:特征、泛型等
现在我们已经掌握了基础知识,让我们深入研究更高级的主题。
特征:接口(有点)
特质有点像其他语言中的接口。它们定义了不同类型可以实现的共享行为。
trait Summary {
fn summarize(&self) -> String;
}
struct Article {
title: String,
content: String,
}
impl Summary for Article {
fn summarize(&self) -> String {
format!("{}: {}", self.title, self.content)
}
}
这里,我们为 Article 结构体定义并实现了一个 Summary trait。现在,任何文章都可以被概括了。Trait 对于编写通用且可复用的代码非常有效。
泛型:编写灵活的代码
泛型允许您编写适用于任何数据类型的函数和类型。
fn largest<T: PartialOrd>(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
}
此函数适用于任何可比较的类型 T。PartialOrd 部分是 trait 绑定的,这意味着 T 必须实现 PartialOrd trait,才能进行排序比较。
实用技巧:编写符合 Rust 语言习惯的程序
-
使用 rustfmt:Rust 内置了格式化程序,可以让你的代码看起来更简洁。只需在项目目录中运行 cargo fmt 即可。
-
使用 Rust Analyzer:这款强大的 IDE 扩展提供代码补全、重构等功能。它就像一位对 Rust 了如指掌的助手。
-
Clippy:这是一款 Rust 的 linter,可以捕获常见错误并提出改进建议。运行 cargo clippy 即可查看它发现了什么。
结论
这篇简短的文章是为了让您更熟悉 Rust。
我有一系列关于这些特定主题的免费视频。
您可以在这里查看
文章来源:https://dev.to/francescoxx/getting-familiar-with-rusts-syntax-35cd