據說,Rust語言語法的高門檻是勸退很多人上手的主要原因。 確實,Rust語言希望解決 C/C++ 手工管理記憶體的問題,但是又不想引入類似golang,java的GC機制。 因此,為了能讓編譯器能夠在編譯階段檢查出潛在的記憶體問題,Rust的語法上就多了一些其他語言所沒有的規則,這些規則讓上手Rust ...
據說,Rust語言語法的高門檻是勸退很多人上手的主要原因。
確實,Rust語言希望解決 C/C++ 手工管理記憶體的問題,但是又不想引入類似golang,java的GC機制。
因此,為了能讓編譯器能夠在編譯階段檢查出潛在的記憶體問題,Rust的語法上就多了一些其他語言所沒有的規則,這些規則讓上手Rust的難度提高了不少。
我是覺得,學習一門編程語言,不一定要弄懂其中的所有概念才能開始寫代碼,就像我們學習外語,掌握了基本幾句話之後其實就可以開始對話練習。
這篇極簡教程的目的,其實不只是針對Rust,任何編程語言都可以有這麼一個極簡教程,讓大家可以儘快用這種編程語言先把代碼寫起來,邊寫邊學,不斷加深對語言的理解。
因為是極簡教程,大綱也很簡單,主要3部分:
- 工程管理
- 變數定義
- 流程式控制制
《C程式設計語言》中有一句很經典話:程式 = 數據結構 + 演算法
上面的【變數定義】其實就是數據結構,【流程式控制制】就是演算法,有了這2部分其實就可以編寫各種功能。
最後加了【工程管理】這部分,一方面是因為Rust的管理工具Cargo是它的重要特色之一;另一方面,良好的代碼組織是完成複雜功能的基礎,畢竟我們學習Rust,最後是希望用它去完成實際的業務功能的。
工程管理
通過 cargo 創建 Rust項目,可以更好的管理項目的依賴,打包和升級等等。
所以,儘管是極簡教程,還是希望能用 cargo 來管理項目。
Rust項目一般有兩種類型,可執行文件和庫。
我們一般做的軟體或者工具最終都會發佈成可執行文件,給用戶使用。
而庫不能單獨運行,一般是對重要的功能進行封裝,然後作為其他軟體的一部分來使用。
創建可執行文件
cargo new rust-examples
tree . # 工程結構如下
.
├── Cargo.toml # 這裡是工程的配置
└── src
└── main.rs
其中 main.rs 是啟動文件,學習Rust各種語法的時候,可以將代碼寫在 main.ts 中進行測試。
創建庫
cargo new --lib rust-lib
tree .
.
├── Cargo.toml # 這裡是工程的配置
└── src
└── lib.rs
這裡的 lib.rs 和 main.rs 不同,它是不能直接運行的,需要通過裡面的測試代碼來運行相應的功能。
變數定義
Rust的基本類型和其他語言大同小異,熟悉其他語言(C/C++, golang等)的話,很容易理解。
這裡我們構造一個簡單的學生成績管理系統,通過定義學生信息,演示下Rust中基本類型以及枚舉和結構體的使用。
cargo new stu_manager
在 src/main.rs 中定義並列印學生信息。
#[derive(Debug)]
enum Sex {
Boy,
Girl,
}
struct Student {
name: String, // 姓名
age: u16, // 年齡
sex: Sex, // 性別
score: f32, // 成績
}
fn main() {
let students: [Student; 2] = [
Student {
name: String::from("boy01"),
age: 18,
sex: Sex::Boy,
score: 61.5,
},
Student {
name: String::from("girl01"),
age: 16,
sex: Sex::Girl,
score: 91.5,
},
];
display(&students[0]);
display(&students[1]);
}
fn display(stu: &Student) {
println!(
"name: {}, age: {}, sex:{:?}, score: {}",
stu.name, stu.age, stu.sex, stu.score
);
}
說明:
#[derive(Debug)]
這個是為了列印enum,否則enum類型是不能直接列印的。struct Student
這個結構體中定義了常用的基本類型的使用方式。let students: [Student; 2]
Rust數組的定義方式。
與其他語言相比,Rust變數的一個新的概念就是所有權和借用規則,我的另一篇博客中有介紹,這裡不在贅述。
Rust所有權和借用規則示例
除此之外,使用的方式和其他語言區別不大,上面示例中,創建了2個學生信息,並且分別列印出其中各個欄位的信息。
$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.01s
Running `target/debug/stu_manager`
name: boy01, age: 18, sex:Boy, score: 61.5
name: girl01, age: 16, sex:Girl, score: 91.5
流程式控制制
掌握了變數定義,可以組織我們的數據,再掌握Rust中的流程式控制制方法,那麼,就能實現實際的業務功能了。
流程式控制制主要兩種:分支和迴圈。
分支
Rust的分支語法有 if
和match
兩種方式。
繼續完善上面的例子,我們增加一個根據成績區分優良中差的函數,用 if
的方式來判斷分支。
fn check_score(stu: &Student) {
if stu.score >= 90.0 {
println!("學員:{}, 成績優秀", stu.name);
} else if stu.score < 90.0 && stu.score >= 75.0 {
println!("學員:{}, 成績良好", stu.name);
} else if stu.score < 75.0 && stu.score >= 60.0 {
println!("學員:{}, 成績中等", stu.name);
} else {
println!("學員:{}, 成績不合格!!!", stu.name);
}
}
再增加一個判斷性別的函數,用match
的方式來判斷分支。
fn check_sex(stu: &Student) {
match stu.sex {
Sex::Boy => println!("學員: {} 是男生", stu.name),
Sex::Girl => println!("學員: {} 是女生", stu.name),
}
}
迴圈
Rust 迴圈主要有3種方式:
- loop 無限迴圈,自己控制迴圈退出
- while 條件迴圈
- for 條件迴圈
下麵用3種迴圈方式分別列印學生信息,學生成績信息以及學生性別信息。
// loop 迴圈示例
let mut count = 0;
loop {
if count == students.len() {
break;
}
display(&students[count]);
count += 1;
}
// while 迴圈示例
count = 0;
while count < students.len() {
check_score(&students[count]);
count += 1;
}
// for 迴圈示例
for stu in students {
check_sex(&stu);
}
3種迴圈中,還是 for
迴圈最為見解,這也是我們使用最多的迴圈方式。
總結
這裡可以看出,只要會任何一種編程語言,幾乎不需要太多Rust特有的知識,我們也可以用Rust來編寫代碼。
當然,Rust的優勢,比如記憶體安全和高性能,這裡並沒有體現。
這篇博客的目的是希望能夠儘快將Rust用起來,用起來之後,遇到問題解決問題,在用的過程中逐步理解Rust的高階概念和語法,一步一步成為Rust高手。
附錄
完整的示例如下:
#[derive(Debug)]
enum Sex {
Boy,
Girl,
}
struct Student {
name: String, // 姓名
age: u16, // 年齡
sex: Sex, // 性別
score: f32, // 成績
}
fn main() {
let students: [Student; 2] = [
Student {
name: String::from("boy01"),
age: 18,
sex: Sex::Boy,
score: 61.5,
},
Student {
name: String::from("girl01"),
age: 16,
sex: Sex::Girl,
score: 91.5,
},
];
// loop 迴圈示例
let mut count = 0;
loop {
if count == students.len() {
break;
}
display(&students[count]);
count += 1;
}
// while 迴圈示例
count = 0;
while count < students.len() {
check_score(&students[count]);
count += 1;
}
// for 迴圈示例
for stu in students {
check_sex(&stu);
}
}
fn display(stu: &Student) {
println!(
"name: {}, age: {}, sex:{:?}, score: {}",
stu.name, stu.age, stu.sex, stu.score
);
}
fn check_score(stu: &Student) {
if stu.score > 100.0 {
println!("學員:{}, 成績錯誤", stu.name);
} else if stu.score <= 100.0 && stu.score >= 90.0 {
println!("學員:{}, 成績優秀", stu.name);
} else if stu.score < 90.0 && stu.score >= 75.0 {
println!("學員:{}, 成績良好", stu.name);
} else if stu.score < 75.0 && stu.score >= 60.0 {
println!("學員:{}, 成績中等", stu.name);
} else {
println!("學員:{}, 成績不合格!!!", stu.name);
}
}
fn check_sex(stu: &Student) {
match stu.sex {
Sex::Boy => println!("學員: {} 是男生", stu.name),
Sex::Girl => println!("學員: {} 是女生", stu.name),
}
}
$ cargo run
Compiling stu_manager v0.1.0 (/home/wangyubin/projects/rust/stu_manager)
Finished dev [unoptimized + debuginfo] target(s) in 0.60s
Running `target/debug/stu_manager`
name: boy01, age: 18, sex:Boy, score: 61.5
name: girl01, age: 16, sex:Girl, score: 91.5
學員:boy01, 成績中等
學員:girl01, 成績優秀
學員: boy01 是男生
學員: girl01 是女生