[易学易懂系列|rustlang语言|零基础|快速入门|(6)|变量绑定]

释放双眼,带上耳机,听听看~!

[易学易懂系列|rustlang语言|零基础|快速入门|(6)]

有意思的基础知识

变量绑定


我们现在回过头来看看,一些基础知识。

因为理解了前面的重要概念:所有权,借用,生命周期。

我们现在看基础知识就很简单了。

先看变量定义:


1
2
3
4
5
6
7
8
1let a = true;//rust不要求显式定义类型,编译器女王自动会根据上下文和用途,来自动定义类型,这里              //是:bool  
2let b: bool = true;//如果显式定义类型,则通过:分号+类型  
3​  
4let (x, y) = (1, 2);  
5​  
6let mut z = 5;  
7z = 6;
8

在rust,这些变量定义叫变量绑定。

为什么这样说?因为rust的变量,默认是不可变的。

如果,要变成可变变量,要用关键字:mut。

那如果是常量呢?

看代码:

常量(const):


1
2
1const N: i32 = 5;
2

静态变量(static):


1
2
1static N: i32 = 5;
2

常量与静态变量的区别是:

1.常量const在内存没有固定地址,而静态变量static的地址是固定的。

2.静态变量一般用在全局变量,一般写在代码最上方,在函数体外,常量可以定义在函数内。

3.一般最好用const来定义常量,因为它地址不是固定的,可以让编译器优化。

布尔值(bool):


1
2
3
4
5
1let x = true;  
2let y: bool = false;  
3​  
4// ⭐️ no TRUE, FALSE, 1, 0
5

字符(char):


1
2
3
4
5
1let x = 'x';  
2let y = '?';  
3​  
4// ⭐️ no "x", only single quotes
5

布尔值,类型关键字:bool,值:true 或者false,注意是小写。

字符类型,用的是单引号。因为rust是从底层支持unicode,所以char
占位4 byte

数组(arrays):


1
2
3
4
5
6
7
8
9
10
11
1let a = [1, 2, 3]; // a[0] = 1, a[1] = 2, a[2] = 3  
2let mut b = [1, 2, 3];  
3​  
4let c: [i32; 0] = []; //[Type; NO of elements] -> [] /empty array  
5let d: [i32; 3] = [1, 2, 3];  
6​  
7let e = ["my value"; 3]; //["my value", "my value", "my value"];  
8​  
9println!("{:?}", a); //[1, 2, 3]  
10println!("{:#?}", a);
11

数组主要用来存放相同类型的数据,它的
长度是固定的,也是默认不可变的(长度和内容都不可变)
。如果,用mut来定义,它的长度也是不可变的,但数组内的数据可以变。

如果我想缓存一些不同类型的数据,怎么办?

用元组(tuples):


1
2
3
4
5
6
7
8
9
10
11
12
13
14
1let a = (1, 1.5, true, 'a', "Hello, world!");  
2// a.0 = 1, a.1 = 1.5, a.2 = true, a.3 = 'a', a.4 = "Hello, world!"  
3​  
4let b: (i32, f64) = (1, 1.5);  
5​  
6let (c, d) = b; // c = 1, d = 1.5  
7let (e, _, _, _, f) = a; //e = 1, f = "Hello, world!", _ indicates not interested of that item  
8​  
9let g = (0,); //single-element tuple  
10​  
11let h = (b, (2, 4), 5); //((1, 1.5), (2, 4), 5)  
12​  
13println!("{:?}", a); //(1, 1.5, true, 'a', "Hello, world!")
14

元组主要用来存放不同类型的数据,它的
长度是固定的,也是默认不可变的(长度和内容都不可变)
。如果,用mut来定义,它的长度也是不可变的,数组内的数据可以变,但
变化的值与之前的值的类型要保持一致

切片(slice):


1
2
3
4
5
6
7
8
9
10
1let a: [i32; 4] = [1, 2, 3, 4];//Parent Array  
2​  
3let b: &[i32] = &a; //Slicing whole array  
4let c = &a[0..4]; // From 0th position to 4th(excluding)  
5let d = &a[..]; //Slicing whole array  
6​  
7let e = &a[1..3]; //[2, 3]  
8let f = &a[1..]; //[2, 3, 4]  
9let g = &a[..3]; //[1, 2, 3]
10

切片,要rust中来说,就是其他数据结构(主要是数组)的
可变长度
的引用或视图。

字符串(str):


1
2
3
1let a = "Hello, world."; //a: &'static str  
2let b: &str = "你好, 世界!";
3

在rust,str类型准确来说,是字符串切片。是最基本的字符串类型。

我们来看看下面两种写法是一样的:


1
2
3
4
5
1let hello = "Hello, world!";  
2​  
3// with an explicit type annotation  
4let hello: &'static str = "Hello, world!";
5

我们看到,hello变量的生命周期注解是:'static,说明它的生命周期是跟整个程序的生命周期一样。

它是借用类型:&str,说明它是从字符串 "Hello, world!",借用过来的。

当然它还有一种写法:


1
2
3
4
5
6
1let hello = String::from("Hello, world!");  
2let mut hello = String::from("Hello, ");  
3​  
4hello.push('w');  
5hello.push_str("orld!");
6

这里直接用方法:
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里分配内存的。我们来看看代码吧:


1
2
3
4
1let s1 = String::from("hello");//从堆heap分配内存给"hello",并绑定到s1  
2let s2 = s1;  
3println!("{}, world!", s1);
4

第一行代码:


1
2
1let s1 = String::from("hello");//从堆heap分配内存给"hello",并绑定到s1
2

在Rust里的内存分配是这样的:

[易学易懂系列|rustlang语言|零基础|快速入门|(6)|变量绑定]

Rust在stack里分配一个空间给s1,分别存放:

1.指向堆heap地址的指针:ptr

2.长度变量len

3.容量变更capacity

现在我们看第二行代码:


1
2
1let s2 = s1;
2

把s1绑定到s2,Rust也会在栈stack分配内存空间给s2(因为stack内存分配是简单且成本低的,运行时runtime会及时回收),而我们知道Rust中的
所有权
概念,所以这时数据的所有权从s1
移动
到s2,如下图:

[易学易懂系列|rustlang语言|零基础|快速入门|(6)|变量绑定]

从上图可以看出,现在s1已经没有对数据"hello"拥有所有权,所以第三行代码:


1
2
1println!("{}, world!", s1);//complier error
2

编译器女王会给你一个错误 :


1
2
3
4
5
6
7
8
9
10
11
12
1error[E0382]: use of moved value: `s1`  
2--> src/main.rs:5:28  
3|  
43 |     let s2 = s1;  
5|         -- value moved here  
64 |  
75 |     println!("{}, world!", s1);  
8|                            ^^ value used here after move  
9|  
10 = note: move occurs because `s1` has type `std::string::String`, which does  
11 not implement the `Copy` trait
12

那要怎么样,才不会报错呢?用clone:


1
2
3
4
5
1let s1 = String::from("hello");  
2let s2 = s1.clone();  
3​  
4println!("s1 = {}, s2 = {}", s1, s2);
5

它在内存中的分配,是这样的:

[易学易懂系列|rustlang语言|零基础|快速入门|(6)|变量绑定]

以上,希望对你有用。


1
2
1如果遇到什么问题,欢迎加入:rust新手群,在这里我可以提供一些简单的帮助,加微信:360369487,注明:博客园+rust
2

本人精通java高并发,DDD,微服务等技术实践,以及python,golang技术栈。 本人姓名郭莹城,坐标深圳,前IBM架构师、咨询师、敏捷开发技术教练,前IBM区块链研究小组成员、十四年架构设计工作经验,《区块链核心技术与应用》作者之一, 现有成熟团队提供区块链开发相关业务(公链,交易所,钱包,Dapp,智能合约)。 工作微信&QQ:360369487,交易所开发与区块链钱包开发业务,加我注明:博客园+开发,想学习golang和rust的同学,也可以加我微信,备注:博客园+golang或博客园+rust,谢谢!

给TA打赏
共{{data.count}}人
人已打赏
安全技术

C/C++内存泄漏及检测

2022-1-11 12:36:11

安全漏洞

快讯:Apache 全系爆拒绝服务漏洞

2011-8-25 11:12:22

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索