所以,你決定要學習 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
}
這裡,s1 在移動到 s2 後不再擁有該字串。
如果您之後嘗試使用 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 是對 s1 的reference
。 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 語句就像是類固醇的開關。這是我最喜歡的語言部分之一,因為它非常強大且富有表現力。
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 提供了?
操作員。這是傳播錯誤的簡寫。
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 特徵。現在,任何文章都可以進行總結。 Traits 對於編寫通用和可重複使用的程式碼來說非常強大。
泛型可讓您編寫適用於任何資料類型的函數和類型。
fn largest<T: PartialOrd>(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
}
此函數適用於任何可以比較的類型 T。 PartialOrd 部分是特徵綁定的,這意味著 T 必須實作 PartialOrd 特徵,它允許進行排序比較。
使用 rustfmt:Rust 有一個內建的格式化程序,可以讓你的程式碼看起來清晰。只需在專案目錄中執行 Cargo fmt 即可。
使用 Rust 分析器:這個強大的 IDE 擴充功能提供程式碼補全、重構等功能。這就像擁有一個對 Rust 瞭如指掌的助手。
Clippy:這是 Rust 的一個 linter,可以捕捉常見錯誤並提出改進建議。執行 Cargo Clippy 看看它發現了什麼。
這篇簡短的文章旨在讓您更加熟悉 Rust。
我有一系列關於這些特定主題的免費影片。
你可以在這裡查看
原文出處:https://dev.to/francescoxx/getting-familiar-with-rusts-syntax-35cd