**變量綁定**
* * *
我們現在回過頭來看看,一些基礎知識。
因為理解了前面的重要概念:所有權,借用,生命周期。
我們現在看基礎知識就很簡單了。
先看變量定義:
~~~
let a = true;//rust不要求顯式定義類型,編譯器女王自動會根據上下文和用途,來自動定義類型,這里 ? ? ? ? ? ? //是:bool
let b: bool = true;//如果顯式定義類型,則通過:分號+類型
?
let (x, y) = (1, 2);
?
let mut z = 5;
z = 6;
~~~
在rust,這些變量定義叫變量綁定。
為什么這樣說?因為rust的變量,默認是不可變的。
如果,要變成可變變量,要用關鍵字:mut。
那如果是常量呢?
看代碼:
常量(const):
~~~
const N: i32 = 5;
~~~
靜態變量(static):
~~~
static N: i32 = 5;
~~~
常量與靜態變量的區別是:
1.常量const在內存沒有固定地址,而靜態變量static的地址是固定的。
2.靜態變量一般用在全局變量,一般寫在代碼最上方,在函數體外,常量可以定義在函數內。
3.一般最好用const來定義常量,因為它地址不是固定的,可以讓編譯器優化。
布爾值(bool):
~~~
let x = true;
let y: bool = false;
?
// ?? no TRUE, FALSE, 1, 0
~~~
字符(char):
~~~
let x = 'x';
let y = '??';
?
// ?? no "x", only single quotes
~~~
布爾值,類型關鍵字:bool,值:true 或者false,注意是小寫。
字符類型,用的是單引號。因為rust是從底層支持unicode,所以char**占位4 byte**。
數組(arrays):
~~~
let a = [1, 2, 3]; // a[0] = 1, a[1] = 2, a[2] = 3
let mut b = [1, 2, 3];
?
let c: [i32; 0] = []; //[Type; NO of elements] -> [] /empty array
let d: [i32; 3] = [1, 2, 3];
?
let e = ["my value"; 3]; //["my value", "my value", "my value"];
?
println!("{:?}", a); //[1, 2, 3]
println!("{:#?}", a);
~~~
數組主要用來存放相同類型的數據,它的**長度是固定的,也是默認不可變的(長度和內容都不可變)**。如果,用mut來定義,它的長度也是不可變的,但數組內的數據可以變。
如果我想緩存一些不同類型的數據,怎么辦?
用元組(tuples):
~~~
let a = (1, 1.5, true, 'a', "Hello, world!");
// a.0 = 1, a.1 = 1.5, a.2 = true, a.3 = 'a', a.4 = "Hello, world!"
?
let b: (i32, f64) = (1, 1.5);
?
let (c, d) = b; // c = 1, d = 1.5
let (e, _, _, _, f) = a; //e = 1, f = "Hello, world!", _ indicates not interested of that item
?
let g = (0,); //single-element tuple
?
let h = (b, (2, 4), 5); //((1, 1.5), (2, 4), 5)
?
println!("{:?}", a); //(1, 1.5, true, 'a', "Hello, world!")
~~~
元組主要用來存放不同類型的數據,它的**長度是固定的,也是默認不可變的(長度和內容都不可變)**。如果,用mut來定義,它的長度也是不可變的,數組內的數據可以變,但**變化的值與之前的值的類型要保持一致**。
切片(slice):
~~~
let a: [i32; 4] = [1, 2, 3, 4];//Parent Array
?
let b: &[i32] = &a; //Slicing whole array
let c = &a[0..4]; // From 0th position to 4th(excluding)
let d = &a[..]; //Slicing whole array
?
let e = &a[1..3]; //[2, 3]
let f = &a[1..]; //[2, 3, 4]
let g = &a[..3]; //[1, 2, 3]
~~~
切片,要rust中來說,就是其他數據結構(主要是數組)的**可變長度**的引用或視圖。
字符串(str):
~~~
let a = "Hello, world."; //a: &'static str
let b: &str = "你好, 世界!";
~~~
在rust,str類型準確來說,是字符串切片。是最基本的字符串類型。
我們來看看下面兩種寫法是一樣的:
~~~
let hello = "Hello, world!";
?
// with an explicit type annotation
let hello: &'static str = "Hello, world!";
~~~
我們看到,hello變量的生命周期注解是:'static,說明它的生命周期是跟整個程序的生命周期一樣。
它是借用類型:&str,說明它是從字符串 "Hello, world!",借用過來的。
當然它還有一種寫法:
~~~
let hello = String::from("Hello, world!");
let mut hello = String::from("Hello, ");
?
hello.push('w');
hello.push_str("orld!");
~~~
這里直接用方法:**String::from()**來構建。
str 和 String之間的區別:
**1.String是一個可變的、堆(heap)上分配的UTF-8的字節符串。**
**2.str是字符串切片,就像vector 切片一樣,它包括:指針+長度,也就是說它是一個字符串變量(這個字符串已經從堆heap上分配內存,像String類型數據或字面值數據string literal )的“視圖”;如果是從String解引用而來的,則指向堆上,如果是字面值,則指向靜態內存。**
一般來說,
如果你相擁有所有權(ownership),就用String定義;
如果想直接借用(沒有所有權,有借就要有還),就用&str;
注意:**字符串String是堆(heap)分配的,它是不定長的。**
說到這里,我想再深入來講講在Rust語言中String的內存分配原理。這很有意思。
其實,跟java一樣,String都是從堆heap里分配內存的。我們來看看代碼吧:
~~~
let s1 = String::from("hello");//從堆heap分配內存給"hello",并綁定到s1
let s2 = s1;
println!("{}, world!", s1);
~~~
第一行代碼:
~~~
let s1 = String::from("hello");//從堆heap分配內存給"hello",并綁定到s1
~~~
在Rust里的內存分配是這樣的:

Rust在stack里分配一個空間給s1,分別存放:
1.指向堆heap地址的指針:ptr
2.長度變量len
3.容量變更capacity
現在我們看第二行代碼:
~~~
let s2 = s1;
~~~
把s1綁定到s2,Rust也會在棧stack分配內存空間給s2(因為stack內存分配是簡單且成本低的,運行時runtime會及時回收),而我們知道Rust中的**所有權**概念,所以這時數據的所有權從s1**移動**到s2,如下圖:

從上圖可以看出,現在s1已經沒有對數據"hello"擁有所有權,所以第三行代碼:
~~~
println!("{}, world!", s1);//complier error
~~~
編譯器女王會給你一個錯誤 :
~~~
error[E0382]: use of moved value: `s1`
--> src/main.rs:5:28
|
3 | ? ? let s2 = s1;
| ? ? ? ? -- value moved here
4 |
5 | ? ? println!("{}, world!", s1);
| ? ? ? ? ? ? ? ? ? ? ? ? ? ^^ value used here after move
|
?= note: move occurs because `s1` has type `std::string::String`, which does
?not implement the `Copy` trait
~~~
那要怎么樣,才不會報錯呢?用clone:
~~~
let s1 = String::from("hello");
let s2 = s1.clone();
?
println!("s1 = {}, s2 = {}", s1, s2);
~~~
它在內存中的分配,是這樣的:

以上,希望對你有用。
- rustlang語言零基礎快速入門(1)開篇
- rustlang語言零基礎快速入門(2)VSCODE配置
- rustlang語言零基礎快速入門(3)所有權Ownership
- rustlang語言零基礎快速入門(4)
- rustlang語言零基礎快速入門(5)
- rustlang語言零基礎快速入門(6)變量綁定
- rustlang語言零基礎快速入門(7)函數Functions與閉包Closure
- rustlang語言零基礎快速入門(8)Operators操作符
- rustlang語言零基礎快速入門(9)Control Flows流程控制
- rustlang語言零基礎快速入門(10)Vectors容器
- rustlang語言零基礎快速入門(11)Structs結構體
- rustlang語言零基礎快速入門(12)Enums枚舉
- rustlang語言零基礎快速入門(13)Generics泛型
- rustlang語言零基礎快速入門(14)Impls & Traits實現與特征
- rustlang語言零基礎快速入門(15)Unit Testing單元測試
- rustlang語言零基礎快速入門(16)代碼組織與模塊化
- rustlang語言零基礎快速入門(17)裝箱crates
- rustlang語言零基礎快速入門(18)use關鍵詞
- rustlang語言零基礎快速入門(19)多線程
- rustlang語言零基礎快速入門(20)錯誤處理
- rustlang語言零基礎快速入門(21)智能指針
- rustlang語言零基礎快速入門(22)宏Macro
- rustlang語言零基礎快速入門(23)實戰1:猜數字游戲
- rustlang語言零基礎快速入門(24)實戰2:命令行工具minigrep(1)
- rustlang語言零基礎快速入門(25)實戰2:命令行工具minigrep(2)
- rustlang語言零基礎快速入門(26)實戰3:Http服務器
- rustlang語言零基礎快速入門(26)實戰3:Http服務器(多線程版本)
- rustlang語言零基礎快速入門(27)實戰4:從零實現BTC區塊鏈
- rustlang語言零基礎快速入門(28)實戰5:實現BTC價格轉換工具
- rustlang語言零基礎快速入門(29)實戰6:BDD工具cucumber_rust