Rust 一門賦予每個人構建可靠且高效軟件能力的語言。具有高性能、可靠性、和生產力三大特點。
接下來就從安裝,開發環境搭建、基礎概念和語法了解三個部分介紹第一周學習Rust的情況。默認為Mac環境
0、安裝Rust
優先建議使用rustup工具進行安裝操作
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
另外如果本地已經安裝了較低的版本,則可以通過rustup update
進行版本升級操作,經過一陣子的等待之后,rust安裝文案提示成功即可,還需要配置環境變量
可以在用戶當前的根目錄下的.bash_profile
文件末尾添加如下配置
# rust
export PATH=$PATH:$HOME/.cargo/bin
最后進行source .bash_profile
刷新即可。通過如下操作檢驗是否安裝成功,參數一定得是大寫的V
? ~ cargo -V
cargo 1.51.0 (43b129a20 2021-03-16)
? ~ rustc -V
rustc 1.51.0 (2fd73fabe 2021-03-23)
cargo Rust構建工具和包管理器,在安裝Rust的時候,會自動安裝Cargo
-
cargo build
構建項目 -
cargo run
運行項目 -
cargo test
測試項目 -
cargo doc
構建項目文檔 -
cargo publish
將當前的庫發布到cartes.io 上
至此整個的運行環境就已經搭建完畢
1、開發環境搭建
VS Code
作為開發環境,安裝好Rust的相關插件,插件rls
,,
- Rust代碼支持,rls
- Rust包版本管理工具,crates
-
TOML文件高亮支持
image.png
在終端環境輸入cargo new rust-learn
就會自動的創建rust-learn項目,其中的src文件包含了入口文件main.rs
運行也就可以直接在終端輸入cargo run
即可,出現的warning只是警告信息,并不會影響程序的執行,而Error則會停止服務的運行
至此,已經可以開始進行demo的編寫,語法的學習以及正常的代碼開發工作了。
2、基礎知識學習
Rust的突擊學習一周,感覺Rust是個大雜燴,包含了多種語言的相關特點,例如:
- 變量聲明的時候采用了JS的規則,可是其又是一個
強類型的語言
,Rust可以手動自定義類型也可以根據具體數據推測其數據類型; - 默認是不可變的,如果需要變成可變的則需要手動添加
mut可變
關鍵字,這個和scala類似; - 內存管理則吸收了C和Java的特點,C里面需要手動申請和釋放內存,而Java則是由JVM統一管理,Rust則是自動申請內存,而在當前的作用域完成之后,自動的添加相關內存釋放的操作;
- 有類似scala的trait特性,其初步理解和接口比較接近,默認返回一個數據的時候可以不用寫
return關鍵字
- 有類似C語言的
&
指針的概念,為數據的所有權做好足夠的約束
說了這么多,接下來就來學習一個Rust吧~
數據類型
Rust是一門強類型的語言,其類型分為整型
,浮點型
,布爾類型
,字符類型
幾種類型,在使用的時候都應該帶上具體是8位還是16位的相關說明,而不是和java一樣,完全由JVM屏蔽了其對象大小的細節情況,從另一個角度來看,也可以節省內存吧。
- 整型
位長度 | 有符號 | 無符號 | 表達形式 |
---|---|---|---|
8-bit | i8 | u8 | 0o77,0o16 |
16-bit | i16 | u16 | 0xff,0x12 |
32-bit | i32 | u32 | - |
64-bit | i64 | u64 | - |
128-bit | i128 | u128 | - |
arch | isize | usize | - |
- 浮點型
就f32
和f64
兩種類型
- bool布爾類型
就true
和false
兩種
- 字符型
char
字符類型,一般是4個字節,因為是4個字節,那么就可以支持中文、日文、emoji表情等數據,使用UTF-8作為編碼格式,注意使用GBK之后導致的亂碼情況,使用單引號括起來
不可變,可變,重影,常量
代碼Demo如下
fn test01() {
let a = "Hello";
// 1、a = "zhangsan"; 不可變對象
// 會提示錯誤 「cannot assign twice to immutable variable」
let a = "lisi";
// 2、重影
println!("a:{}", a);
let mut b = "Hello";
b = "zhangsan";
// 3、可變對象
println!("b:{}", b);
const Count:i32 = 32;
// 4、常量
println!("count:{}", Count);
}
運行結果:
Finished dev [unoptimized + debuginfo] target(s) in 1.22s
Running `target/debug/rust-learn`
a:lisi
b:zhangsan
使用let
關鍵字進行聲明變量,而且默認是不可變對象,如果再對其進行賦值操作,則會出現錯誤「cannot assign twice to immutable variable」,這點和對Java的認識是不一樣的,一旦固定下來了,就是不可變的??梢允褂?code>let mut關鍵字設置可變對象。重影Shadowing則是給當前賦值對象重新指向一個數據
總結&理解:
默認不可變可能是Rust從高并發安全的角度設計的,盡量減少對數據的改值操作
重影shadowing則是一個新的概念,此外并沒有接觸,還是應該從內存管理的角度理解吧,如代碼中2的操作之后,之前的Hello對象,就會被系統自動的remove
掉
常量必須指定數據類型
,而且應該是具體的數據表達式,而不是什么方法之類的,此外也不可用于重影操作
Rust對內存、安全的要求有點嚴苛
String、&str和所有權
先看看如下代碼
fn test02() {
fn cp(content: String) {
println!("content:{}", content);
}
let a = "Hello World"; // 1
cp(a);
println!("a:{}", a);
}
代碼很簡單,大致也能知道只需要傳入一個String的參數數據進行打印操作,然后把數據本身再打印一次,如果正常的話,則會輸出兩遍結果。但是意外的是,這個代碼出現了2個錯誤
- 錯誤1 cp方法出現錯誤 expected struct
String
, found&str
報錯意思很明顯,希望傳入的String類型,但是實際傳入的則是&str,這就引出來了一個情況,1處的代碼生命的對象實際上是&str
類型,而不是期望的String
。
- String是一個可變的、堆上分配的UTF-8的字節緩沖區,存在len()和capacity()方法
- str是一個不可變的固定長度的字符串,只有一個len()方法
-
&
不能僅僅理解成指針,實際上是租借borrowed的意思
image.png
那么怎么解決問題呢,把方法定義和傳入的參數定義統一即可,都改成String,或者&str,甚至&String等
&str ==> String的幾種方案
let a = "hello";
let b1 = a.to_string();
let b2 = String::from(a);
let b3 = a.to_owned();
String ==> &str的幾種方案
let a = String::from("hello");
let b1 = &a;
let b2 = a.as_str();
- 錯誤2:最后的一行出現錯誤 value borrowed here after move
意思很清楚,數值被使用的時候已經被remove掉了,就無法使用,為什么呢?
主要原因是因為傳入到cp方法的參數是對象a本身,a的作用域已經被移動到方法內部了,隨著作用域的改變,在方法外的打印操作就屬于作用域外的調用執行,所以就被理解成remove
掉了,那么自然就會出現失敗的情況。再看一個下面的代碼,加強對所有權的理解
fn main() {
let s = String::from("hello");
// s 被聲明有效
takes_ownership(s);
// s 的值被當作參數傳入函數
// 所以可以當作 s 已經被移動,從這里開始已經無效
let x = 5;
// x 被聲明有效
makes_copy(x);
// x 的值被當作參數傳入函數
// 但 x 是基本類型,依然有效
// 在這里依然可以使用 x 卻不能使用 s
} // 函數結束, x 無效, 然后是 s. 但 s 已被移動, 所以不用被釋放
fn takes_ownership(some_string: String) {
// 一個 String 參數 some_string 傳入,有效
println!("{}", some_string);
} // 函數結束, 參數 some_string 在這里釋放
fn makes_copy(some_integer: i32) {
// 一個 i32 參數 some_integer 傳入,有效
println!("{}", some_integer);
} // 函數結束, 參數 some_integer 是基本類型, 無需釋放
fn main() {
let s1 = gives_ownership();
// gives_ownership 移動它的返回值到 s1
let s2 = String::from("hello");
// s2 被聲明有效
let s3 = takes_and_gives_back(s2);
// s2 被當作參數移動, s3 獲得返回值所有權
} // s3 無效被釋放, s2 被移動, s1 無效被釋放.
fn gives_ownership() -> String {
let some_string = String::from("hello");
// some_string 被聲明有效
return some_string;
// some_string 被當作返回值移動出函數
}
fn takes_and_gives_back(a_string: String) -> String {
// a_string 被聲明有效
a_string // a_string 被當作返回值移出函數
}
最后自己嘗試著用各自方法解決一下demo中出現的錯誤
總結&理解:
String和str是完全不同的兩種數據結構,默認定義一個字符串是str的類型
在使用方法的時候,寫的參數一定需要注意到,是否有使用引用,是否被提前移除等情況
暫時就先總結這么多,后面的繼續學習和更新