常見程序設計概念
函數
函數在rust中無處不在, 對於rust程序來講, main
函數是許多程序的入口, 之前我們知道, 建立一個函數的關鍵字是 fn
rust使用下劃線命名法來命名, 這個之前也有提到過
我們來看下面的程序
fn main() {
println!("Hello, world!");
another_function();
}
fn another_function() {
println!("hello another_function!");
}
rust中, 函數的範圍由 {}
指定, 也就是說, 這段代碼中有兩個函數, main 和 another_function, 程序的入口是 main, 在 main 中調用了 another_function, 當我們從上往下看的時候, 會發現在 another_function 未定義之前就調用了 another_function, 這樣也是可以的, rust 並不會報錯, 只要你存在, 不管在哪裏都可以.
在函數中邏輯是從上到下執行的, 因此會先打印 Hello, world!, 在執行 another_function, another_function 中會打印 hello another_function!
➜ fun git:(master) ✗ cargo run
Compiling fun v0.1.0 (/Users/Work/Code/Rust/student/fun)
Finished dev [unoptimized + debuginfo] target(s) in 1.00s
Running `target/debug/fun`
Hello, world!
hello another_function!
函數的參數
很多時候, 函數需要根據參數來進行操作, rust需要在建立函數時定義參數的名稱與類型, 寫在函數名後的 ()
中
fn main() {
another_function(5);
}
fn another_function(x: i32) {
println!("x = {}", x);
}
➜ fun git:(master) ✗ cargo run
Compiling fun v0.1.0 (/Users/Work/Code/Rust/student/fun)
Finished dev [unoptimized + debuginfo] target(s) in 0.40s
Running `target/debug/fun`
x = 5
對於函數 another_function
來講, 我們設置了一個參數 x
, 他是 i32
類型, 需要注意的是, 我們必須對每個參數都說明類型, 不然會導致編譯錯誤
當某個函數的需要多個參數時, 我們這樣寫
fn main() {
another_function(1, 2);
}
fn another_function(x: i32, y: i32) {
println!("x = {}", x);
println!("y is {}", y);
}
函數的內容包含了陳述式和表達式
函數的內容是由一系列的陳述式(statements)和在後面的可選的表達式(expression)組成的, rust是基於表達式的語言, 下面我們來介紹這兩個的區別
陳述式是一系列動作的指令, 陳述式不回傳任何數據
表達式則是通過邏輯處理來產生結果, 也就是說表達式返回數據
比如代碼
fn main() {
let a = 1;
}
這裏的mian函數本身是一個陳述式, 因爲他不返回任何數據
這裏的 let a = 1;
也是一個陳述式, 因爲他不返回任何數據, 因爲他不返回任何數據, 所以你不能接受他的返回數據, 例如
fn main() {
let b = (let a = 1);
}
這就會導致編譯出錯, 因爲let a = 1
是一個陳述式, 他不返回任何數據, 但是你試圖使用返回值來作爲 b 的值
而表達式則會給出結果, 比如 1+1
會返回2
, 表達式可以是陳述式的一部分, 比如 let a = 1
中的1
就是一個表達式, 他返回了一個1, 同時我們使用{}
產生的作用域也是一個表達式, 例如
fn main() {
let x = 5;
let y = {
let x = 3;
x + 1
};
println!("y = {}", y);
}
其中, 表達式
let y = {
let x = 3;
x + 1
};
會返回4
, 此時使用 y 接收就能獲得 4, 這是因爲 x + 1
後面並沒有帶上 ;
, 如果你加上了 ;
則不會返回結果, 這是必須要記住的
函數返回值
函數的返回值定義是->
, 寫在接收參數的()
後, 同樣的要定義他的類型. 但是無需命名
fn five() -> i32 {
5
}
fn main() {
let x = five();
println!("x = {}", x);
}
需要注意, five 函數裏直接寫了5, 但是沒有跟;
, 所以他會返回5, 同時 five 定義了返回值是一個, 類型是 i32
同時 main 中定義了 x 來接受 five 的返回值, 所以 x 爲 5, 同時函數 five 爲表達式
➜ fun git:(master) ✗ cargo run
Compiling fun v0.1.0 (/Users/Work/Code/Rust/student/fun)
Finished dev [unoptimized + debuginfo] target(s) in 0.57s
Running `target/debug/fun`
x = 5
當我們爲 5 加上 ;
, five 就變成了 陳述式, 此時在編譯時就會報錯
➜ fun git:(master) ✗ cargo run
Compiling fun v0.1.0 (/Users/Work/Code/Rust/student/fun)
error[E0308]: mismatched types
--> src/main.rs:1:14
|
1 | fn five() -> i32 {
| ---- ^^^ expected `i32`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression
2 | 5;
| - help: consider removing this semicolon
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.
error: could not compile `fun`.
To learn more, run the command again with --verbose.
當這個函數有多個返回值, 則使用()
包裹
fn five() -> (i32, i32) {
(5, 6)
}
fn main() {
let x = five();
println!("x0 = {}, x1 = {}", x.0, x.1);
}
➜ fun git:(master) ✗ cargo run
Compiling fun v0.1.0 (/Users/Work/Code/Rust/student/fun)
Finished dev [unoptimized + debuginfo] target(s) in 0.25s
Running `target/debug/fun`
x0 = 5, x1 = 6
註釋
一個好的程序, 註釋並不可少. 多寫註釋(comments)對自己和他人都有好處
編譯器在編譯時會將註釋部分去除, 所以不用擔心會增加編譯文件的大小
rust的註釋使用 //
, 一般會在 //
後加上一個空格
// 這裏是註釋
需要注意的是, //
標識改行爲註釋, 因此 //
後一直到本行結束之間的所有東西都被編譯器認爲是註釋
當需要多行註釋時, 爲每一行都加上 //
即可
// 註釋1
// 註釋2
註釋也可以加在某一行代碼的結束
fn main() {
let x = 3; // 註釋
}
控制流程
在程序中, 一個流程通常有多個分支, 我們需要在某個時候根據某個條件來決定怎麼做
if表達式
if表達式根據條件的不同執行不同的代碼. 當滿足條件時就執行這段代碼, 不滿足時就不執行
fn main(){
let a = 5;
if a < 5{
println!("<5")
}else{
println!(">=5")
}
}
這裏我們判斷a是否小於5, 根據不同的情況來打印不同的結果
需要注意的是, 其中某個分支的邏輯必須使用 {}
包裹
並且, a<5 返回的是一個bool
, rust中if表達式只能使用bool
來進行判斷, 加入我們將代碼修改成
fn main(){
let a = 5;
if a{
println!("<5")
}else{
println!(">=5")
}
}
則會導致編譯失敗, 這是因爲a是int
而不是bool
➜ fun git:(master) ✗ cargo run
Compiling fun v0.1.0 (/Users/Work/Code/Rust/student/fun)
error[E0308]: mismatched types
--> src/main.rs:3:8
|
3 | if a{
| ^ expected `bool`, found integer
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.
error: could not compile `fun`.
To learn more, run the command again with --verbose.
rust並不會將其他非bool值自動轉化成bool
使用 else if 處理多個情況
使用多個else if
和if
和else
一起使用來處理多個分支
fn main(){
let a = 5;
if a == 4{
println!("4")
}else if a == 5{
println!("5")
}else if a == 5{
println!("51")
}else{
println!("not")
}
}
需要注意的是, 在這個代碼塊中, 判斷只會成功一次, 也就是說, 在這個多重判斷中, 即使a可以爲true
兩次, 也只會執行一次, 也就是隻會打印5
而不是51
當你有多個分支時, 不推薦使用大量的 else if
, 這樣會導致代碼看起來不友好, 後面會推薦使用match
在let中使用if
fn main(){
let t = true;
let s = if t{
5
}else{
6
};
println!("{}", s)
}
這裏就是, 我們在賦值的時候, 根據t的不同來返回不同的值給s, 這裏注意, 寫一個數字本身就是一個表達式, 也就是說如果t爲true
, s就爲5, 其他爲6
➜ fun git:(master) ✗ cargo run
Compiling fun v0.1.0 (/Users/Work/Code/Rust/student/fun)
Finished dev [unoptimized + debuginfo] target(s) in 0.36s
Running `target/debug/fun`
5
這種寫法有一個限制, 就是if的返回值必須是同一類型, s不可能有可能是int
有可能是str
, 例如
fn main(){
let t = true;
let s = if t{
5
}else{
"6"
};
println!("{}", s)
}
這種就會報錯
➜ fun git:(master) ✗ cargo run
Compiling fun v0.1.0 (/Users/Work/Code/Rust/student/fun)
error[E0308]: `if` and `else` have incompatible types
--> src/main.rs:6:9
|
3 | let s = if t{
| _____________-
4 | | 5
| | - expected because of this
5 | | }else{
6 | | "6"
| | ^^^ expected integer, found `&str`
7 | | };
| |_____- `if` and `else` have incompatible types
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.
error: could not compile `fun`.
To learn more, run the command again with --verbose.
因爲rust在編譯時就必須準確的知道s的類型, 但是s可能會有兩種類型, 這樣會導致代碼有可能出現問題
循環
循環可以重複的執行某段代碼
rust有三種循環: loop
/while
/for
loop
loop關鍵字會讓rust一直重複執行代碼一直到明確的要求結束
看以下代碼
fn main(){
loop{
println!("a")
}
}
此程序是死循環, 不停的打印字符串a, 因爲沒有使用關鍵字break
, 所以不會終止循環
從循環返回
fn main(){
let mut c = 0;
let r = loop{
c += 1;
if c == 10{
break c*2;
}
};
println!("{}", r)
}
我們可以定義一個變量來接受某個循環的返回值
while循環
我們使用循環時, 多會使用一個大的嵌套, 比如loop{}
, 其實rust中的while
可以在特定的場景下減少代碼, 比如
fn main(){
let mut c = 4;
while c != 0{
c -= 1;
println!("{}", c)
}
println!("OK")
}
while可以與一個判斷條件一起使用, 比如這裏就是判斷c!=0
, 如果c!=0
爲false
時則退出循環. 而在循環內則對c進行-1, 代碼邏輯等同於
fn main(){
let mut c = 4;
loop {
c -= 1;
println!("{}", c);
if c == 0{
break;
};
}
println!("OK")
}
for循環
使用遍歷
來描述for可能更爲準確, for並不是死循環, 而是遍歷完成就結束
fn main(){
let c = [1, 2, 3, 4];
for i in c.iter(){
println!("{}", i)
}
}
這裏我們遍歷c這個數組, 注意c.iter()
可以生成一個range
, 每次拋出c的一個元素, 使用for可以防止索引超出範圍
➜ fun git:(master) ✗ cargo run
Compiling fun v0.1.0 (/Users/Work/Code/Rust/student/fun)
Finished dev [unoptimized + debuginfo] target(s) in 0.42s
Running `target/debug/fun`
1
2
3
4
再比如
fn main(){
let c = [1, 2, 3, 4];
for i in c.iter().rev(){
println!("{}", i)
}
}
rev
方法可以翻轉一個range
, 因此會輸出4-1
➜ fun git:(master) ✗ cargo run
Compiling fun v0.1.0 (/Users/Work/Code/Rust/student/fun)
Finished dev [unoptimized + debuginfo] target(s) in 0.32s
Running `target/debug/fun`
4
3
2
1
練習
編寫程序實現攝氏度與華氏度之間的轉換
use std::io; // 引入標準庫
fn main(){
loop {
println!("選擇轉換模式:
1: 攝氏度轉華氏度
2: 華氏度轉攝氏度
其他任意鍵: 退出
>>>");
let mut model = String::new();
io::stdin().read_line(&mut model).expect("讀取失敗"); // 如果獲取錯誤打印警告
if model.trim() == "1"{
println!("輸入你要轉換的攝氏度>>>");
let mut c = String::new();
io::stdin().read_line(&mut c).expect("讀取失敗"); // 如果獲取錯誤打印警告
let c: f32 = match c.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("輸入了一個無法解析的字符串");
continue;
},
};
let f = transformation_centigrade(c);
println!("攝氏度:{}對應的華氏度是:{}", c, f);
}
else if model.trim() == "2"{
println!("輸入你要轉換的華氏度>>>");
let mut f = String::new();
io::stdin().read_line(&mut f).expect("讀取失敗"); // 如果獲取錯誤打印警告
let f: f32 = match f.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("輸入了一個無法解析的字符串");
continue;
},
};
let c = transformation_fahrenheit_degree(f);
println!("華氏度:{}對應的攝氏度是:{}", f, c);
}
else{
println!("exit.");
break;
}
}
}
// 華氏度2攝氏度
fn transformation_fahrenheit_degree(f: f32) -> f32{
return (f-32.0)/1.8;
}
// 攝氏度2華氏度
fn transformation_centigrade(c: f32) -> f32{
return 32.0+c*1.8;
}
運行
➜ fun git:(master) ✗ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/fun`
選擇轉換模式:
1: 攝氏度轉華氏度
2: 華氏度轉攝氏度
其他任意鍵: 退出
>>>
1
輸入你要轉換的攝氏度>>>
222
攝氏度:222對應的華氏度是:431.59998
選擇轉換模式:
1: 攝氏度轉華氏度
2: 華氏度轉攝氏度
其他任意鍵: 退出
>>>
2
輸入你要轉換的華氏度>>>
4444
華氏度:4444對應的攝氏度是:2451.111
選擇轉換模式:
1: 攝氏度轉華氏度
2: 華氏度轉攝氏度
其他任意鍵: 退出
>>>
q
exit.
生成 n 階斐波那契數列
use std::io; // 引入標準庫
fn main(){
loop {
println!("輸入期望的n>>>");
let mut n = String::new();
io::stdin().read_line(&mut n).expect("讀取失敗");
let n: usize = match n.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("輸入了一個無法解析的字符串");
continue;
},
};
let mut c: usize = 0;
let mut s0 = 0;
let mut s1 = 1;
println!("結果: ");
loop{
if c == n{
break;
};
let mut f = 0;
if c == 0{
f = 0;
}else{
f = s0+s1;
}
s0 = s1;
s1 = f;
c+=1;
print!("{} ", f)
}
println!("")
}
}
運行
➜ fun git:(master) ✗ cargo run
Compiling fun v0.1.0 (/Users/Work/Code/Rust/student/fun)
warning: value assigned to `f` is never read
--> src/main.rs:23:21
|
23 | let mut f = 0;
| ^
|
= note: `#[warn(unused_assignments)]` on by default
= help: maybe it is overwritten before being read?
Finished dev [unoptimized + debuginfo] target(s) in 0.48s
Running `target/debug/fun`
輸入期望的n>>>
3
結果:
0 1 1
輸入期望的n>>>
1
結果:
0
打印歌曲 “The Twelve Days of Christmas” 的歌詞, 使用循環
fn main(){
let days = ["first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth", "ninth", "tenth", "eleventh", "twelfth"];
let gift = ["a partridge in a pear tree", "two turtle doves", "three French hens", "four calling birds", "five golden rings", "six geese a-laying", "seven swans a-swimming", "eight maids a-milking", "nine ladies dancing", "ten lords a-leaping", "eleven pipers piping", "twelve drummers drumming"];
let mut n = 0;
println!("The Twelve days of Christmas");
for d in days.iter(){
print!("On the {} day of Christmas, my true love sent to me: ", d);
let mut gi = n;
loop{
if gi != 0{
print!("{}, ", gift[gi]);
gi -= 1;
}else if n == 0{
print!("{} \n", gift[gi]);
break;
}else{
print!("and {} \n", gift[gi]);
break;
}
};
n += 1
};
}
運行
The Twelve days of Christmas
On the first day of Christmas, my true love sent to me: a partridge in a pear tree
On the second day of Christmas, my true love sent to me: two turtle doves, and a partridge in a pear tree
On the third day of Christmas, my true love sent to me: three French hens, two turtle doves, and a partridge in a pear tree
On the fourth day of Christmas, my true love sent to me: four calling birds, three French hens, two turtle doves, and a partridge in a pear tree
On the fifth day of Christmas, my true love sent to me: five golden rings, four calling birds, three French hens, two turtle doves, and a partridge in a pear tree
On the sixth day of Christmas, my true love sent to me: six geese a-laying, five golden rings, four calling birds, three French hens, two turtle doves, and a partridge in a pear tree
On the seventh day of Christmas, my true love sent to me: seven swans a-swimming, six geese a-laying, five golden rings, four calling birds, three French hens, two turtle doves, and a partridge in a pear tree
On the eighth day of Christmas, my true love sent to me: eight maids a-milking, seven swans a-swimming, six geese a-laying, five golden rings, four calling birds, three French hens, two turtle doves, and a partridge in a pear tree
On the ninth day of Christmas, my true love sent to me: nine ladies dancing, eight maids a-milking, seven swans a-swimming, six geese a-laying, five golden rings, four calling birds, three French hens, two turtle doves, and a partridge in a pear tree
On the tenth day of Christmas, my true love sent to me: ten lords a-leaping, nine ladies dancing, eight maids a-milking, seven swans a-swimming, six geese a-laying, five golden rings, four calling birds, three French hens, two turtle doves, and a partridge in a pear tree
On the eleventh day of Christmas, my true love sent to me: eleven pipers piping, ten lords a-leaping, nine ladies dancing, eight maids a-milking, seven swans a-swimming, six geese a-laying, five golden rings, four calling birds, three French hens, two turtle doves, and a partridge in a pear tree
On the twelfth day of Christmas, my true love sent to me: twelve drummers drumming, eleven pipers piping, ten lords a-leaping, nine ladies dancing, eight maids a-milking, seven swans a-swimming, six geese a-laying, five golden rings, four calling birds, three French hens, two turtle doves, and a partridge in a pear tree