Rust 從入門到精通05-數(shù)據(jù)類型
靜態(tài)類型(statically typed)語言,也就是說在編譯時(shí)就必須知道所有變量的類型。
在 Rust 中,每一個(gè)值都屬于某一個(gè) 數(shù)據(jù)類型(data type),分為兩大類:
①、標(biāo)量(scalar):整型、浮點(diǎn)型、布爾類型、字符類型
②、復(fù)合(compound):元祖(tuple)、數(shù)組(array)、結(jié)構(gòu)體(struct)
1、標(biāo)量scalar
每個(gè)類型有一個(gè)單獨(dú)的值。
1.1 整型
表示沒有小數(shù)部分的數(shù)字,分為有符號(hào)(以 i 開頭)和無符號(hào)(以 u 開頭)整型。
數(shù)字類型的默認(rèn)類型是 i32。
長度有符號(hào)無符號(hào)8-biti8
u8
16-biti16
u16
32-biti32
u32
64-biti64
u64
128-biti128
u128
archisize
usize
每一個(gè)有符號(hào)的整型可以儲(chǔ)存包含從 -($2^{n - 1}$) 到 $2^{n - 1}$ - 1 在內(nèi)的數(shù)字,這里 n 是整型定義的長度。所以 i8
可以儲(chǔ)存從 -$2^7$到 $2^7$ - 1 在內(nèi)的數(shù)字,也就是從 -128 到 127。無符號(hào)的變體可以儲(chǔ)存從 0 到 $2^{n - 1}$ 的數(shù)字,所以 u8
可以儲(chǔ)存從 0 到 $2^8 - 1$ 的數(shù)字,也就是從 0 到 255。
另外,isize
和 usize
類型依賴運(yùn)行程序的計(jì)算機(jī)架構(gòu):64 位架構(gòu)上它們是 64 位的, 32 位架構(gòu)上它們是 32 位的。
1.1.1 所有數(shù)字字面量,可以在任意地方添加下劃線_
?fn int_test(){
? ? ?//所有數(shù)字字面量,可以在任意地方添加下劃線_
? ? ?let x : u32 = 1_2_3;
? ? ?let y = x + 1;
? ? ?//打印結(jié)果為 124
? ? ?println!("{}",y);
?}
1.1.2 字面量可以跟類型后綴,表示數(shù)字具體類型
?//字面量可以跟類型后綴,表示數(shù)字具體類型
?fn int_test2(){
? ? ?let x = 123i32;
? ? ?let y = x + 1;
? ? ?//打印結(jié)果為 124
? ? ?println!("{}",y);
?}
1.1.3 直接對(duì)整型字面量調(diào)用函數(shù)
?//直接對(duì)整型字面量調(diào)用函數(shù)
?fn int_test3(){
? ? ?let x : i32 = 9;
? ? ?//打印結(jié)果為 729
? ? ?println!("9 power 3 = {}",x.pow(3));
?}
1.1.4 整數(shù)溢出
Rust 對(duì)于整數(shù)溢出的處理方式如下: ①、默認(rèn)情況下,在debug模式下編譯器會(huì)自動(dòng)插入整數(shù)溢出檢查,一旦發(fā)生溢出,則會(huì)引發(fā) panic; ②、在 release 模式下,不檢查整數(shù)溢出,而是自動(dòng)舍棄高位的方式。
1.1.5 如何選擇
通常默認(rèn)類型 i32 即可,它通常是最快的。
1.2 浮點(diǎn)
Rust 有兩個(gè)原生的 浮點(diǎn)數(shù)(floating-point numbers)類型,它們是帶小數(shù)點(diǎn)的數(shù)字。是基于 IEEE 754-2008 標(biāo)準(zhǔn)的浮點(diǎn)類型,分別是 f32
和 f64
,分別占 32 位和 64 位。默認(rèn)類型是 f64
,因?yàn)樵诂F(xiàn)代 CPU 中,它與 f32
速度幾乎一樣,不過精度更高。
?fn float_test(){
? ? ?//123.0 f32類型
? ? ?let f1 = 123.0f32;
? ? ?//0.1 f64類型
? ? ?let f2 = 0.1f64;
?}
1.3 布爾類型
布爾類型(bool)代表的是“是”和“否”的二值邏輯。它有兩個(gè)值:
true和false
一般用在邏輯表達(dá)式中,可以執(zhí)行“與”“或”“非”等運(yùn)算。
?fn bool_test(){
? ? ?let x = true;
? ? ?//取反運(yùn)算
? ? ?let y = !x;
?
? ? ?//邏輯與,帶短路功能
? ? ?let z = x && y;
?
? ? ?//邏輯或,帶短路功能
? ? ?let z = x || y;
?
?}
1.4、字符類型
字符類型由 char 表示。它可以描述任何一個(gè)符合 unicode 標(biāo)準(zhǔn)的字符值。在代碼中,單個(gè)的字符字面量用單引號(hào)包圍(不同于字符串用):
1.4.1 4個(gè)字節(jié)字符
?let heart_eyed_cat = '??';
因?yàn)?char 類型的設(shè)計(jì)目的是描述任意一個(gè) unicode 字符,因此它占據(jù)的內(nèi)存空間不是1個(gè)字節(jié),而是 4 個(gè)字節(jié)。
這意味著它可以比 ASCII 表示更多內(nèi)容。在 Rust 中,拼音字母(Accented letters),中文、日文、韓文等字符,emoji(繪文字)以及零長度的空白字符都是有效的 char
值。Unicode 標(biāo)量值包含從 U+0000
到 U+D7FF
和 U+E000
到 U+10FFFF
在內(nèi)的值。
1.4.2 1個(gè)字節(jié)字符-u8
let x : u8 = 1;
對(duì)于 ASCII 字符其實(shí)只需要占據(jù)一個(gè)字節(jié)的空間,因此Rust 提供了單字節(jié)字符字面量來表示 ASCII 字符。
注意:我們還可以通過一個(gè)字母 b 在字符或者字符串前面,代表這個(gè)字面量存儲(chǔ)在 u8 類型數(shù)組中,這樣占用空間比 char 型數(shù)組要小一些。
let x : u8 = 1; let y : u8 = b'A';
2、復(fù)合compound
復(fù)合類型(Compound types)可以將多個(gè)值組合成一個(gè)類型
2.1 元祖(tuple)
①、由圓括號(hào)()包含一組表達(dá)式組成;
②、長度固定,一旦聲明,其長度不會(huì)增大或縮小。
③、rust中可以存放不同類型的數(shù)據(jù)類型
2.1.2 實(shí)例
?fn tuple_test1(){
? ? ?//包含兩個(gè)元素:1和false
? ? ?let a = (1i32,false);
? ? ?//包含兩個(gè)元素:1和元祖,元祖包含兩個(gè)字符1和2
? ? ?let b = (1,("1","2"));
?}
2.1.3 如果元祖只有一個(gè)元素,應(yīng)該添加一個(gè)逗號(hào),用來區(qū)分括號(hào)表達(dá)式和元祖
?//如果元祖只有一個(gè)元素,應(yīng)該添加一個(gè)逗號(hào),用來區(qū)分括號(hào)表達(dá)式和元祖
?fn tuple_test2(){
? ? ?//a 是一個(gè)元祖,只有一個(gè)元素1
? ? ?let a = (1,);
? ? ?//b 是一個(gè)括號(hào)表達(dá)式,它是 i32類型
? ? ?let b = (1);
?}
2.1.4 訪問元祖元素
①、模式匹配解構(gòu)
//元祖:模式匹配
fn tup_test4(){
? ?let tup = (1,1.1,2);
? ?let (x,y,z) = tup;
? ?println!("x={},y={},z={}",x,y,z);
}
②、數(shù)字索引
//元祖:數(shù)字索引
fn tup_test5(){
? ?let tup = (1,1.1,2);
? ?println!("x={},y={},z={}",tup.0,tup.1,tup.2);
}
2.2 數(shù)組(array)
①、由中括號(hào)[] 包含一組表達(dá)式組成;
②、數(shù)組中每個(gè)元素的類型必須相同(元祖tuple可以不同);
③、長度固定,一旦聲明,其長度不會(huì)增大或縮小。
2.2.1 實(shí)例
有三種方式聲明。
//數(shù)組:實(shí)例
fn array_test1(){
? ?//1、省略類型和長度
? ?let a = [1,1,1,1];
? ?//2、聲明類型和長度
? ?let b:[i32;4] = [1,1,1,1];
? ?//3、聲明初始值和長度
? ?let c = [1;4];
? ?println!("{}",a == b);//true
? ?println!("{}",a == c);//true
? ?println!("{}",c == b);//true
}
2.2.2 訪問數(shù)組元素
①、通過下標(biāo)訪問
初始下標(biāo)是0
//數(shù)組:訪問元素
fn array_test2(){
? ?let a = [1,2,3,4];
? ?println!("a[0]={}",a[0]);
? ?println!("a[1]={}",a[1]);
? ?println!("a[2]={}",a[2]);
}
②、通過 get() 方法
注意返回值是 Option<T>
//數(shù)組:訪問元素
fn array_test3(){
? ?let a = [1,2,3,4];
? ?let first = a.get(0);
? ?let last = a.get(4);
? ?println!("{:?}",first);//Some(1)
? ?println!("{:?}",last);//None
}
2.2.3 數(shù)組越界訪問異常
如果聲明的數(shù)組有4個(gè),但是訪問下標(biāo)大于或等于4,則會(huì)在運(yùn)行時(shí)拋出異常(編譯能過)。
//數(shù)組:訪問元素
fn array_test3(){
? ?let a = [1,2,3,4];
? ?println!("a[4]={}",a[4]);
}

2.2.4 避免數(shù)組越界程序崩潰
如果我們不確定讀取數(shù)組的索引是否合法,上面通過索引的方式訪問就會(huì)發(fā)生異常,導(dǎo)致程序奔潰。
為了避免這種情況,我們可以使用 get(index) 的方法來獲取數(shù)組中的元素,其返回值是 Option<T>
//數(shù)組:訪問元素
fn array_test3(){
? ?let a = [1,2,3,4];
? ?let first = a.get(0);
? ?let last = a.get(4);
? ?println!("{:?}",first);//Some(1)
? ?println!("{:?}",last);//None
}
2.3 結(jié)構(gòu)體(struct)
結(jié)構(gòu)體和元祖類似,都可以把多個(gè)類型組合到一起,作為新的類型。 結(jié)構(gòu)體又可以分為三種具體類型:
// 具名結(jié)構(gòu)體
struct Name_Struct {
? ?x : f32,
? ?y : f32,
}
// 元祖結(jié)構(gòu)體
struct Tuple_Struct(f32,f32);
// 單元結(jié)構(gòu)體
struct Unit_Struct;
2.3.1 具名結(jié)構(gòu)體
//結(jié)構(gòu)體
fn struct_test1(){
? ?struct Point{
? ? ? ?x : i32,
? ? ? ?y : i32,
? ?}
? ?let p = Point{x:0,y:0};
? ?println!("{},{}",p.x,p.y);
}
①、每個(gè)元素之間采用逗號(hào)分開,最后一個(gè)逗號(hào)可以省略不寫。 ②、類型依舊跟在冒號(hào)后面,但是不能使用自動(dòng)類型推導(dǎo)功能,必須顯示指定。
局部變量和結(jié)構(gòu)體變量一致,可以省略掉重復(fù)的冒號(hào)初始化
//局部變量和結(jié)構(gòu)體變量一致,可以省略掉重復(fù)的冒號(hào)初始化
fn struct_test2(){
? ?struct Point{
? ? ? ?x : i32,
? ? ? ?y : i32,
? ?}
? ?let x = 10;
? ?let y = 20;
? ?let p = Point{x,y};
? ?println!("{},{}",p.x,p.y);
}
2.3.2 元祖結(jié)構(gòu)體tuple struct
這是前面介紹的 tuple 和 struct 兩種類型的混合,tuple struct 結(jié)構(gòu)有名字,但是成員沒有名字。
名字加圓括號(hào),類型有單獨(dú)的名字,成員沒有單獨(dú)的名字。
fn tuple_struct(){
? ?struct Color (
? ? ? ?i32,
? ? ? ?i32,
? ? ? ?i32
? ?);
}
訪問方法
通過下標(biāo)訪問:
fn tuple_struct(){
? ?struct Color (
? ? ? ?i32,
? ? ? ?i32,
? ? ? ?i32
? ?);
? ?let v1 = Color(1,2,3);
? ?println!("{},{},{}",v1.0,v1.1,v1.2)
}
2.3.3 單元結(jié)構(gòu)體
// 單元結(jié)構(gòu)體
struct Unit_Struct;
單元結(jié)構(gòu)體不會(huì)占用任何內(nèi)存空間。
3、枚舉 enum
如果說 tuple、struct、tuple struct 在 Rust 中代表的是多個(gè)類型的“與”關(guān)系,那么 enurn類型在 Rust 中代表的就是多個(gè)類型的“或”關(guān)系。
Rust 的 enurn 中的每個(gè)元素的定義語法與 struct 的定義語法類似。可以像空結(jié)構(gòu)體一樣,不指定它的類型;也可以像 tuple struct 一樣,用圓括號(hào)加無名成員;還可以像正常結(jié)構(gòu)體一樣,用大括號(hào)加帶名字的成員。
fn main() {
? ?let x = enum_define::Int(12);
? ?let y = enum_define::Float(3.2);
? ?let z = enum_define::Move {x:1,y:2};
? ?let k = enum_define::Color(255,255,255);
? ?match x {
? ? ? ?enum_define::Int(i) => {
? ? ? ? ? ?println!("{}",i);
? ? ? ?},
? ? ? ?enum_define::Float(f) => {
? ? ? ? ? ?println!("{}",f);
? ? ? ?},
? ? ? ?enum_define::Move{x,y} => {
? ? ? ? ? ?println!("{} {}",x,y);
? ? ? ?},
? ? ? ?enum_define::Color(x,y,z) => {
? ? ? ? ? ?println!("{}{}{}",x,y,z);
? ? ? ?}
? ?}
}
enum enum_define{
? ?Int(i32),
? ?Float(f32),
? ?Move{x:i32,y:i32},
? ?Color(i32,i32,i32),
}
4、特殊數(shù)據(jù)類型
4.1 Never 類型
表示不可能返回值的數(shù)據(jù)類型。
①、類型理論中,叫做底類型,底類型不包含任何值,但它可以合一到任何其它類型;
②、Never 類型用感嘆號(hào)“!" 表示;
③、目前還未穩(wěn)定,但是rust內(nèi)部已經(jīng)開始用了。
5、常見錯(cuò)誤
5.1 類型轉(zhuǎn)換必須通過 as 關(guān)鍵字顯式聲明
//類型轉(zhuǎn)換必須通過 as 關(guān)鍵字顯式聲明
fn switch_test(){
? ?let var1 : i8 = 1;
? ?let var2 : i32 = var1;
}
報(bào)錯(cuò)如下:

增加 as 關(guān)鍵字顯示聲明即可。
//類型轉(zhuǎn)換必須通過 as 關(guān)鍵字顯式聲明
fn switch_test(){
? ?let var1 : i8 = 1;
? ?let var2 : i32 = var1 as i32;
}
5.2 復(fù)合數(shù)據(jù)類型允許遞歸,但是不允許直接嵌套
//復(fù)合數(shù)據(jù)類型不允許直接嵌套
fn recursive(){
? ?struct recur {
? ? ? ?data : i32,
? ? ? ?rec : recur
? ?}
}
報(bào)錯(cuò)如下:
