Rust是一门赋予每个人构建可靠且高效软件能力的编程语言。可靠主要体现在安全性上。其高效不仅限于开发效率,它的执行效率也是令人称赞的,是一种少有的兼顾开发效率和执行效率的语言。Rust 语言由 Mozilla 开发,最早发布于 2014 年 9 月。Rust 的编译器是在 MIT License 和 Apache License 2.0 双重协议声明下的免费开源软件。
高性能:Rust 速度惊人且内存利用率极高。由于没有运行时和垃圾回收,它能够胜任对性能要求特别高的服务,可以在嵌入式设备上运行,还能轻松和其他语言集成。
可靠性:Rust 丰富的类型系统和所有权模型保证了内存安全和线程安全,让您在编译期就能够消除各种各样的错误。
生产力:Rust 拥有出色的文档、友好的编译器和清晰的错误提示信息, 还集成了一流的工具——包管理器和构建工具, 智能地自动补全和类型检验的多编辑器支持, 以及自动格式化代码等等。
Rustacean:使用 rust 的攻城狮不叫 ruster 而是叫 Rustacean ,咱也不知道为什么,书上就是这么说的。
以 windows 11 为例
下载 rustup-init.exe ,双击此可执行程序会打开一个命令行程序,此程序引导安装,具体安装过程:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
Rust Visual C
+
+
prerequisites
Rust requires the Microsoft C
+
+
build tools
for
Visual Studio
2013
or
later, but they don't seem to be installed.
The easiest way to acquire the build tools
is
by installing Microsoft
Visual C
+
+
Build Tools
2019
which provides just the Visual C
+
+
build
tools:
https:
/
/
visualstudio.microsoft.com
/
visual
-
cpp
-
build
-
tools
/
Please ensure the Windows
10
SDK
and
the English language pack components
are included when installing the Visual C
+
+
Build Tools.
Alternately, you can install Visual Studio
2019
, Visual Studio
2017
,
Visual Studio
2015
,
or
Visual Studio
2013
and
during install select
the
"C++ tools"
:
https:
/
/
visualstudio.microsoft.com
/
downloads
/
Install the C
+
+
build tools before proceeding.
If you will be targeting the GNU ABI
or
otherwise know what you are
doing then it
is
fine to
continue
installation without the build
tools, but otherwise, install the C
+
+
build tools before proceeding.
Continue? (y
/
N) y
Welcome to Rust!
This will download
and
install the official compiler
for
the Rust
programming language,
and
its package manager, Cargo.
Rustup metadata
and
toolchains will be installed into the Rustup
home directory, located at:
C:\Users\cml\.rustup
This can be modified with the RUSTUP_HOME environment variable.
The Cargo home directory located at:
C:\Users\cml\.cargo
This can be modified with the CARGO_HOME environment variable.
The cargo, rustc, rustup
and
other commands will be added to
Cargo's
bin
directory, located at:
C:\Users\cml\.cargo\
bin
This path will then be added to your PATH environment variable by
modifying the HKEY_CURRENT_USER
/
Environment
/
PATH registry key.
You can uninstall at
any
time with rustup
self
uninstall
and
these changes will be reverted.
Current installation options:
default host triple: x86_64
-
pc
-
windows
-
msvc
default toolchain: stable (default)
profile: default
modify PATH variable: yes
1
) Proceed with installation (default)
2
) Customize installation
3
) Cancel installation
>
info: profile
set
to
'default'
info: default host triple
is
x86_64
-
pc
-
windows
-
msvc
info: syncing channel updates
for
'stable-x86_64-pc-windows-msvc'
info: latest update on
2022
-
01
-
20
, rust version
1.58
.
1
(db9d1b20b
2022
-
01
-
20
)
info: downloading component
'cargo'
3.8
MiB
/
3.8
MiB (
100
%
)
1.7
MiB
/
s
in
2s
ETA:
0s
info: downloading component
'clippy'
1.6
MiB
/
1.6
MiB (
100
%
)
1.5
MiB
/
s
in
1s
ETA:
0s
info: downloading component
'rust-docs'
18.8
MiB
/
18.8
MiB (
100
%
)
3.3
MiB
/
s
in
5s
ETA:
0s
info: downloading component
'rust-std'
22.9
MiB
/
22.9
MiB (
100
%
)
3.2
MiB
/
s
in
7s
ETA:
0s
info: downloading component
'rustc'
65.2
MiB
/
65.2
MiB (
100
%
)
493.2
KiB
/
s
in
1m
14s
ETA:
0s
info: downloading component
'rustfmt'
2.2
MiB
/
2.2
MiB (
100
%
)
631.2
KiB
/
s
in
3s
ETA:
0s
info: installing component
'cargo'
info: installing component
'clippy'
info: installing component
'rust-docs'
18.8
MiB
/
18.8
MiB (
100
%
)
1.9
MiB
/
s
in
6s
ETA:
0s
info: installing component
'rust-std'
22.9
MiB
/
22.9
MiB (
100
%
)
10.5
MiB
/
s
in
2s
ETA:
0s
info: installing component
'rustc'
65.2
MiB
/
65.2
MiB (
100
%
)
12.2
MiB
/
s
in
5s
ETA:
0s
info: installing component
'rustfmt'
info: default toolchain
set
to
'stable-x86_64-pc-windows-msvc'
stable
-
x86_64
-
pc
-
windows
-
msvc installed
-
rustc
1.58
.
1
(db9d1b20b
2022
-
01
-
20
)
Rust
is
installed now. Great!
To get started you may need to restart your current shell.
This would
reload
its PATH environment variable to include
Cargo's
bin
directory (
%
USERPROFILE
%
\.cargo\
bin
).
Press the Enter key to
continue
.
|
命令 | 说明 | 备注 |
---|---|---|
rustup doc | 打开官方指导文档 | |
cargo new projectName | 创建一个rust工程 | 示例:cargo new firstRustProject |
cargo run | 运行rust工程 | |
cargo build | 编译rust工程 | 若增加了依赖,即修改了toml文件,需要重新编译 |
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
fn main() {
println!(
"Hello, world!"
);
/
/
变量默认是不可变的,加上 mut 关键字就可以重新赋值。
let mut x
=
5
;
println!(
"The value of x is {} "
,x);
x
=
6
;
println!(
"The value of x is {} "
,x);
/
/
变量的隐藏
let money
=
100
;
println!(
"money is {}"
,money);
let money
=
money
+
8
;
println!(
"money is {}"
,money);
let money
=
"一百元"
;
println!(
"money is {}"
,money);
/
/
常量使用 const 关键字声明,声明的时候必须指定数据类型,常量名全大写。
/
/
不需要let , 不可使用mut 修饰
const MAX_PIONTS: u32
=
888
;
println!(
"The constant is {}"
,MAX_PIONTS);
let result:char
=
a_function(
88
,
'M'
, false);
println!(
"result is {}"
,result);
}
fn a_function(a:u64,b:char,c:
bool
)
-
> char{
println!(
"a is {}"
,a);
println!(
"b is {}"
,b);
println!(
"c is {}"
,c);
return
'N'
;
}
|
输出:
1
2
3
4
5
6
7
8
9
10
11
|
Hello, world!
The value of x
is
5
The value of x
is
6
money
is
100
money
is
108
money
is
一百元
The constant
is
888
a
is
88
b
is
M
c
is
false
result
is
N
|
整数,浮点,布尔,字符
可以将多个值放到一个数据类型中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
fn main() {
println!(
"Hello, world!"
);
let q
=
3.0
;
let q:f32
=
5.00
;
let w
=
true;
let r:
bool
=
false;
let t
=
'';
let tup :(i32,u64,
bool
)
=
(
88
,
99
,false);
println!(
"元素1:{},元素2:{},元素3:{}"
,tup.
0
, tup.
1
, tup.
2
);
let arr:[u64;
5
]
=
[
1
,
2
,
3
,
5
,
5
];
let arr2
=
[
'E'
;
9
];
println!(
"arr piont 2 is :{}"
,arr[
1
]);
println!(
"arr2 piont 2 is :{}"
,arr2[
1
]);
/
/
Vector:todo
}
|
和大多数编程语言一样, if - else if - else 表示条件语句,在 if 后面不用加括号。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
fn main() {
println!(
"Hello, world!"
);
/
/
条件语句
let a
=
12
;
let b;
if
a >
0
{
b
=
1
;
}
else
if
a <
0
{
b
=
-
1
;
}
else
{
b
=
0
;
}
println!(
"b is {}"
, b);
/
/
三元运算符
let x
=
3
;
let number
=
if
x >
0
{
1
}
else
{
-
1
};
println!(
"number 为 {}"
, number);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
fn main() {
println!(
"Hello, world!"
);
/
/
循环
/
/
while
let mut number
=
1
;
while
number !
=
4
{
println!(
"{}"
, number);
number
+
=
1
;
}
println!(
"while cycle EXIT"
);
/
/
for
-
迭代器
let a
=
[
10
,
20
,
30
,
40
,
50
];
for
i
in
a.
iter
() {
println!(
"元素值为 : {}"
, i);
}
println!(
"for-iter cycle EXIT"
);
/
/
for
-
下标
let b
=
[
10
,
20
,
30
,
40
,
50
];
let mut length
=
b.
len
();
println!(
"b 数组的长度是:{}"
, length);
for
i
in
0.
.length {
println!(
"b[{}] = {}"
, i, b[i]);
}
println!(
"for-index cycle EXIT"
);
/
/
loop 终止循环,并返回一个值
let s
=
[
'R'
,
'U'
,
'N'
,
'O'
,
'B'
];
let mut i
=
0
;
let location
=
loop {
let ch
=
s[i];
if
ch
=
=
'B'
{
break
i;
}
i
+
=
1
;
};
println!(
" \'B\' 的索引为 {}"
, location);
}
|
1
|
println!(
"print a small =_=*"
);
|
花括号中套花括号就可以输出花括号
示例代码:
1
2
|
let mut name
=
String::
from
(
"cml"
);
println!(
"输出中带花括号:{{ {} }}"
, name);
|
以上代码输出:
1
|
输出中带花括号:{ cml }
|
1
|
println!(
"输出一个结构体,a={:?}"
, a);
|
1
2
|
let mut guess
=
String::new();
io::stdin().read_line(&mut guess).expect(
"无法读取行"
);
|
所有权可以理解为命名空间+作用域+指针。
特殊的数据类型--->类型的引用(即指针),使用 & 关键字表示
可变变量也可以有指针
示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
fn main() {
println!(
"Hello, world!"
);
/
/
01.
基本数据类型(值类型)变量在栈空间中可以复制。先给x赋值
9
(let x
=
9
),将x赋值给y等同于直接给y赋值
9
(let y
=
x 等同于let y
=
9
)
let x
=
9
;
let y
=
x;
/
/
x
is
:
9
, y
is
:
9
println!(
"x is :{0} , y is :{1}"
, x, y);
let c1
=
'M'
;
let c2
=
c1;
/
/
c1
is
:M , c2
is
:M
println!(
"c1 is :{0} , c2 is :{1}"
, c1, c2);
/
/
02.
引用类型变量在堆空间中的“值引用”可以复制,但是存储在栈空间的“值”不可复制。因此,引用类型变量仅可被消费一次。
let s1
=
String::
from
(
"hello"
);
let s2
=
s1;
/
/
编译错误:use of moved value: `s1`
/
/
let s3
=
s1;
/
/
编译错误:borrow of moved value: `s1`
/
/
println!(
"s1 is :{0} , s2 is :{1}"
, s1, s2);
/
/
03.
引用类型变量可以通过“克隆”的方式复制。
let h1
=
String::
from
(
"hello"
);
let h2
=
h1.clone();
let h3
=
h1.clone();
/
/
h1
=
hello, h2
=
hello , h3
is
:hello
println!(
"h1 = {}, h2 = {} , h3 is :{}"
, h1, h2, h3);
/
/
04.
特殊的数据类型
-
-
-
>类型的引用(即指针),使用 & 关键字表示
let k1
=
String::
from
(
"hello"
);
/
/
k1为String类型:std::String::String ; k2 为带String类型的指针类型:&std::String:String
let k2
=
&k1;
let k3
=
k2;
/
/
指针存放在栈中,便于理解可以将指针看作“特殊的值类型”。所以虽然k2已经赋值给了k3,任然可以赋值给k4。
let k4
=
k2;
/
/
k1无法赋值给k5,因为k1已经被借用了。这是出于安全考虑,试想如果多个人都可以借用k1,意味着多个人可以修改k1,那势必对正在使用它的人正在进行的工作产生影响
/
/
let k5
=
k1;
/
/
编译错误:`k2`
is
a `&` reference, so the data it refers to cannot be borrowed as mutable
/
/
既然k2是指针类型,那么就不允许被修改,因为k2的“值”本身是借来的,如果修改了,那么势必对正在使用它的人正在进行的工作产生影响
/
/
k2.push_str(
"world"
);
println!(
"k1 is {}, k2 is {}, k3 is :{} , k4 is : {}"
, k1, k2, k3, k4);
/
/
值类型(基本数据类型)变量也可以使用指针,但是一般不建议这样使用
/
/
n1为i32类型;n2为&i32类型
let n1
=
8
;
let n2
=
&n1;
println!(
"n1 is :{} , n2 is :{}"
, n1, n2);
/
/
05.
可变变量的指针。
let mut m1
=
String::
from
(
"run"
);
let m2
=
&mut m1;
m2.push_str(
",world"
);
println!(
"m2 is :{}"
, m2);
/
/
编译错误:cannot borrow `m1` as immutable because it
is
also borrowed as mutable
/
/
借出去后,所有权已不在拥有,所以无法被消费
/
/
println!(
"m1 is :{} , m2 is :{}"
, m1, m2);
/
/
编译错误:cannot borrow `m2` as mutable, as it
is
not
declared as mutable。cannot borrow as mutable
/
/
不可将借来的东西再借给别人
/
/
let m3
=
&mut m2;
/
/
06.rust
中不允许出现空指针
}
|
以上代码输出:
1
2
3
4
5
6
7
|
Hello, world!
x
is
:
9
, y
is
:
9
c1
is
:M , c2
is
:M
h1
=
hello, h2
=
hello , h3
is
:hello
k1
is
hello, k2
is
hello, k3
is
:hello , k4
is
: hello
n1
is
:
8
, n2
is
:
8
m2
is
:run,world
|
切片是指向数据结构(字符串、集合)一部分内容的引用。
不愿意将rust中的切片理解为一种“类型”,实际上也不是;更不愿将rust中的切片理解为一种“集合”。暂且将切片理解成一种对象吧。例如 &s[0..5] 就是获取到了字符串 s 索引从0到5位置的元素,包含0不包含5。
rust中的切片部分主要是要理解索引和下标的概念。
示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
fn main() {
println!(
"Hello, world!"
);
let s
=
String::
from
(
"hello,world."
);
/
/
ss 的数据类型为:&
str
let ss
=
&s[
0.
.
5
];
println!(
"s is : {} , ss is : {}"
, s, ss);
let arr
=
[
1
,
3
,
5
,
7
,
9
];
/
/
start_part 的数据类型为:&[i32]
let start_part
=
&arr[
0.
.
3
];
let end_part
=
&arr[
3.
.];
let full_part
=
&arr[..];
println!(
"arr is : {:?} , start_part is : {:?} , end_part is : {:?} , full_part is : {:?}"
,
arr, start_part, end_part, full_part
);
}
|
以上代码输出:
1
2
3
|
Hello, world!
s
is
: hello,world. , ss
is
: hello
arr
is
: [
1
,
3
,
5
,
7
,
9
] , start_part
is
: [
1
,
3
,
5
] , end_part
is
: [
7
,
9
] , full_part
is
: [
1
,
3
,
5
,
7
,
9
]
|
struct 类似 java 中的类,用来自定义一种数据结构,这种数据结构一般用来描述生活中的某一个对象。struct 中可以包含属性和方法。使用结构体分为两步:首先需要定义一个结构体,然后需要实例化一个结构体,再然后才可以使用。
一旦结构体实例化的时候是可变的,即使用 mut 修饰, 那么结构体中所有的属性都将是可变的。
结构体属性:
示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
/
*
*
*
人
*
/
#[derive(Debug)]
struct Person {
/
/
结构体属性:声明方式,属性名:数据类型;即使是最后一个属性,末尾也要加英文逗号;属性可以是另外一个结构体,但不能是本身;属性可以是一个元组
active:
bool
,
name: String,
email: String,
sign_in_count: u64,
nation: Nation,
parent: Parent,
empty: Empty,
tuple
: (u32, u32),
}
/
*
*
*
国家结构体
*
/
#[derive(Debug)]
struct Nation {
name: String,
area: String,
time_zone: u64,
}
/
*
*
*
父母结构体
*
特殊的结构体,元组结构体。
*
/
#[derive(Debug)]
struct Parent(String, String);
/
*
*
*
空结构体
*
/
#[derive(Debug)]
struct Empty;
fn main() {
println!(
"Hello, world!"
);
/
/
实例化一个结构体
let p1
=
Person {
active: true,
name: String::
from
(
"cml"
),
email: String::
from
(
"cnaylor@163.com"
),
sign_in_count:
99
,
nation: Nation {
name: String::
from
(
"xm"
),
area: String::
from
(
"north"
),
time_zone:
8
,
},
parent: Parent(String::
from
(
"baba"
), String::
from
(
"mama"
)),
empty: Empty,
tuple
: (
8
,
8
),
};
/
/
实例化一个可变结构体
let mut p2
=
Person {
active: true,
name: String::
from
(
"cml"
),
email: String::
from
(
"cnaylor@163.com"
),
sign_in_count:
99
,
nation: Nation {
name: String::
from
(
"xm"
),
area: String::
from
(
"north"
),
time_zone:
8
,
},
parent: Parent(String::
from
(
"baba"
), String::
from
(
"mama"
)),
empty: Empty,
tuple
: (
8
,
8
),
};
/
/
给结构体实例重新赋值
p2.email
=
String::
from
(
"c@163.com"
);
let email
=
&p2.email;
println!(
"p1 is : {:#?} , p2 is : {:#?} , email is :{}"
, p1, p2, email);
#[derive(Debug)]
struct User {
active:
bool
,
username: String,
email: String,
sign_in_count: u64,
}
fn build_user(email: String, username: String)
-
> User {
let u
=
User {
email: email,
/
/
简写。函数参数和结构体属性名相同,可简写
/
/
email,
username,
active: true,
sign_in_count:
1
,
};
/
/
从已创建的结构体实例创建实例
/
/
let u2
=
User { ..u };
let u3
=
User {
email: String::
from
(
"o@163.com"
),
..u
};
return
u3;
}
let u
=
build_user(String::
from
(
"a@163.com"
), String::
from
(
"tom"
));
println!(
"u is : {:?}"
, u);
}
|
以上代码输出:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
Hello, world!
p1
is
: Person {
active: true,
name:
"cml"
,
email:
"cnaylor@163.com"
,
sign_in_count:
99
,
nation: Nation {
time_zone:
8
,
},
parent: Parent(
"baba"
,
"mama"
,
),
empty: Empty,
tuple
: (
8
,
8
,
),
} , email
is
:c@
163.com
u
is
: User { active: true, username:
"tom"
, email:
"o@163.com"
, sign_in_count:
1
}
|
枚举成员:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
use std::fs::
File
;
fn main() {
let f
=
File
::
open
(
"hello.txt"
);
match f {
Ok(
file
)
=
> {
println!(
"File opened successfully."
);
},
Err(err)
=
> {
println!(
"Failed to open the file."
);
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
|
fn main() {
enum Book {
Papery(u32),
Electronic(String)
}
let book
=
Book::Electronic(String::
from
(
"url"
));
if
let Book::Papery(index)
=
book {
println!(
"Papery {}"
, index);
}
else
{
println!(
"Not papery book"
);
}
}
|
Option 是一个标准库中的枚举,用来处理空值(null) 的情况。Option是一个泛型枚举,接受类型 T 。Option 要干的事情和java 中的 optional(java8新特性) 类似。
简言之:
Option 源码:
1
2
3
|
enum Option<T> {
Some(T),
None
,}
|
使用举例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
/
/
整数除法。
fn checked_division(dividend: i32, divisor: i32)
-
> Option<i32> {
if
divisor
=
=
0
{
/
/
失败表示成 `
None
` 取值
None
}
else
{
/
/
结果 Result 被包装到 `Some` 取值中
Some(dividend
/
divisor)
}
}
/
/
此函数处理可能失败的除法
fn try_division(dividend: i32, divisor: i32) {
/
/
`Option` 值可以进行模式匹配,就和其他枚举类型一样
let result
=
checked_division(dividend, divisor);
if
result!
=
Option::
None
{
/
/
获取返回值
println!(
"Nice, result is :{:?}"
, result.unwrap());
}
match checked_division(dividend, divisor) {
None
=
> println!(
"{} / {} failed!"
, dividend, divisor),
Some(quotient)
=
> {
println!(
"{} / {} = {}"
, dividend, divisor, quotient)
}
}
}
fn main() {
try_division(
4
,
2
);
try_division(
1
,
0
);
}
|
以上代码输出:
1
2
3
|
Nice, result
is
:
2
4
/
2
=
2
1
/
0
failed!
|
Rust 标准库中包含一系列被称为 集合(collections)的非常有用的数据结构。大部分其他数据类型都代表一个特定的值,不过集合可以包含多个值。不同于内建的数组和元组类型,集合指向的数据是储存在堆上的,这意味着数据的数量不必在编译时就已知,并且还可以随着程序的运行增长或缩小。
vector 允许我们在一个单独的数据结构中储存多个值,所有值在内存中彼此相邻排列。vector 只能储存相同类型的值。
如果借助枚举,有时候 vector 也可以变相存储不同类型的值。
示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
fn main() {
println!(
"Hello, world!"
);
/
/
新建一个 vector
let mut v: Vec<i32>
=
Vec::new();
let v2
=
vec![
1
,
2
,
3
];
println!(
"v is : {:?} , v2 is : {:?}"
, v, v2);
/
/
更新
v.push(
888
);
v.push(
111
);
v.push(
222
);
v.push(
333
);
/
/
删除
v.remove(
0
);
/
/
查询
let two
=
v[
1
];
let two2
=
&v[
1
];
/
/
get方法返回的是Option
let three
=
v.get(
2
);
println!(
"two is : {} , three is : {:?}"
, two, three);
/
/
遍历
let arr
=
vec![
100
,
32
,
57
];
for
i
in
&arr {
println!(
"arr item for --> {}"
, i);
}
/
/
借助枚举,vector中可以存储不同的数据类型
let row
=
vec![
SpreadsheetCell::
Int
(
3
),
SpreadsheetCell::Text(String::
from
(
"blue"
)),
SpreadsheetCell::
Float
(
10.12
),
];
let s
=
&row[
1
];
println!(
"row is : {:?} , s is : {:?} "
, row, s);
}
#[derive(Debug)]
enum SpreadsheetCell {
Int
(i32),
Float
(f64),
Text(String),
}
|
以上代码输出:
1
2
3
4
5
6
7
|
Hello, world!
v
is
: [] , v2
is
: [
1
,
2
,
3
]
two
is
:
222
, three
is
: Some(
333
)
arr item
for
-
-
>
100
arr item
for
-
-
>
32
arr item
for
-
-
>
57
row
is
: [
Int
(
3
), Text(
"blue"
),
Float
(
10.12
)] , s
is
: Text(
"blue"
)
|
你没有看错,rust 中,字符串也是集合,是什么集合呢? 是“字符”的集合。注意:上面提到过字符是 rust 的基础标量类型,但是字符串不是标量类型,而是集合类型。
更新字符串的方式:
示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
fn main() {
println!(
"Hello, world!"
);
/
/
创建
let mut s
=
String::new();
let data
=
"initial contents"
;
let ss
=
data.to_string();
let sss
=
String::
from
(
"你好!"
);
println!(
"s is : {} , ss is : {} , sss is : {}"
, s, ss, sss);
/
/
更新
let mut word
=
String::
from
(
"Aa"
);
word.push_str(
"Bb"
);
word.push(
'C'
);
word
+
=
"cDd"
;
word
=
format
!(
"{}{}-{}"
, word,
"Ee"
,
"Ff"
);
println!(
"final word is : {} "
, word);
/
/
不要使用下标访问
let p
=
String::
from
(
"hello"
);
let p1
=
&p[
0.
.
3
];
let k
=
String::
from
(
"你好我是陈明亮"
);
let k1
=
&k[
0.
.
3
];
/
/
输出结果可能不是大多数人预期:p1
is
: hel , k1
is
: 你
println!(
"p1 is : {} , k1 is : {}"
, p1, k1);
/
/
遍历
word
+
=
"北京"
;
for
i
in
word.chars() {
println!(
"word ---> item is :{}"
, i);
}
}
|
以上代码输出:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
Hello, world!
s
is
: , ss
is
: initial contents , sss
is
: 你好!
final word
is
: AaBbCcDdEe
-
Ff
p1
is
: hel , k1
is
: 你
word
-
-
-
> item
is
:A
word
-
-
-
> item
is
:a
word
-
-
-
> item
is
:B
word
-
-
-
> item
is
:b
word
-
-
-
> item
is
:C
word
-
-
-
> item
is
:c
word
-
-
-
> item
is
:D
word
-
-
-
> item
is
:d
word
-
-
-
> item
is
:E
word
-
-
-
> item
is
:e
word
-
-
-
> item
is
:
-
word
-
-
-
> item
is
:F
word
-
-
-
> item
is
:f
word
-
-
-
> item
is
:b
word
-
-
-
> item
is
:C
word
-
-
-
> item
is
:c
word
-
-
-
> item
is
:D
word
-
-
-
> item
is
:d
word
-
-
-
> item
is
:E
word
-
-
-
> item
is
:e
word
-
-
-
> item
is
:
-
word
-
-
-
> item
is
:F
word
-
-
-
> item
is
:f
word
-
-
-
> item
is
:北
word
-
-
-
> item
is
:京
|
讲道理,rust 中的代码组织相比 java 、CSharp 这些所谓高级语言,要复杂的多。大概包含两个部分:命名空间和访问权限。
rust 中针对包管理主要有三个概念:包(package),箱(crate),模块(module)。三个概念形成一个树状结构,包中包含箱,箱中包含模块。
Rust 中默认所有项(函数、方法、结构体、枚举、模块和常量)都是私有的。父模块中的项不能使用子模块中的私有项,但是子模块中的项可以使用他们父模块中的项。
关键字:
如果 other_rs_file.rs 中定义了子 module , main.rs 仅需导入顶层 module (即 mod other_rs_file) ,使用 mod::子mod::对象名 访问其中对象
rust 默认将rs文件识别为一个 module 处理, 但是无法将文件夹识别为一个 module ,所以如果我们要访问entity文件夹中dept.rs文件中的对象,需要在entiry文件夹中新增一个mod.rs 并在其中定义 dept module ,访问其中对象的时候同样仅需导入顶层 mod ,然后通过 entity::dept::Dept 方式访问 dept 对象。
工程目录文件结构:
1
2
3
4
5
|
│ main.rs
│ other_rs_file.rs
└─entity
dept.rs
mod.rs
|
示例代码:
other_rs_file.rs:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
pub mod other {
#[derive(Debug)]
pub struct User {
/
/
结构体属性默认是私有的,若外部需要访问需添加pub关键字
pub name: String,
pub age: i32,
}
fn init_user(n: String)
-
> User {
let u
=
User {
name: String::
from
(n),
age:
100
,
};
return
u;
}
pub fn add_user(n: String, a: i32)
-
>
bool
{
return
false;
}
/
/
枚举不用给元素添加 pub ,只要枚举是公开的,里面元素就是公开的
#[derive(Debug)]
pub enum IpAddrKind {
IPV4,
IPV6,
}
}
|
dept.rs:
1
2
3
4
5
|
#[derive(Debug)]
pub struct Dept {
pub name: String,
pub no: i32,
}
|
mod.rs:
1
|
pub mod dept;
|
main.rs:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
mod other_rs_file;
mod entity;
fn main() {
println!(
"Hello, world!"
);
/
*
*
main.rs 同级 rs文件
*
引入其他rs文件,需先导入module ,通过mod名称(即文件名称)::对象名称 访问其内部对象
*
如果在其他文件内部定义了子 module , 导入方式不变,访问方式:mod名称::子mod名称::对象名称
*
导入时候只导入顶层mod
*
/
let u
=
other_rs_file::other::User {
name: String::
from
(
"cml"
),
age:
9
,
};
println!(
"实例化另外一个rs文件中定义的结构体,u is: {:?}"
, u);
/
*
*
main.rs 上级文件夹 rs 文件
*
rust 默认将rs文件识别为一个 module 处理, 但是无法将文件夹识别为一个 module ,所以如果我们要访问entity文件夹中dept.rs文件中的对象,需要在entiry文件夹中新增一个mod.rs 并在其中定义 dept module。
*
/
let d
=
entity::dept::Dept{
name:String::
from
(
"技术部门"
),
no:
5
,
};
println!(
"实例化另外一个rs文件中定义的结构体,d is: {:?}"
, d);
}
|
main输出:
1
2
3
|
Hello, world!
实例化另外一个rs文件中定义的结构体,u
is
: User { name:
"cml"
, age:
9
}
实例化另外一个rs文件中定义的结构体,d
is
: Dept { name:
"技术部门"
, no:
5
}
|
示例代码:
Cargo.toml:
1
2
3
4
5
6
7
8
|
[package]
name
=
"Crate"
version
=
"0.1.0"
edition
=
"2021"
[dependencies]
rand
=
"0.5.5"
|
main.rs:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
/
*
*
导入标准库
*
/
use std::fmt::Result as FmtResult;
use std::io::Result as IoResult;
/
/
use std::
cmp
::Ordering
use std::
*
;
use std::{
self
,
cmp
::Ordering, io};
/
*
*
导入外部第三方库
*
/
use rand::{thread_rng, Rng};
fn main() {
println!(
"Hello, world!"
);
let fmtr
=
FmtResult::Ok;
let ior
=
IoResult::Ok(
"成功"
);
println!(
"fmtr 无法打印 , ior is : {:?}"
, ior);
/
/
生成随机数
let mut rng
=
thread_rng();
let x: u32
=
rng.gen();
println!(
"x is :{}"
, x);
}
|
工程结构:
1
2
3
4
5
|
│ Cargo.lock
│ Cargo.toml
│ hello.txt
├─src
│ main.rs
|
示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
use std::fs;
use std::fs::
File
;
use std::io;
use std::io::Read;
fn main() {
println!(
"Hello, world!"
);
let mut f
=
File
::
open
(
"hello.txt"
);
match f {
Ok(
file
)
=
> {
println!(
"File opened successfully."
);
}
Err(err)
=
> {
/
/
打开失败
println!(
"Failed to open the file."
);
match err.kind() {
/
/
打开失败原因,不存在情况创建
io::ErrorKind::NotFound
=
> {
println!(
"File Not Found. soon create . "
);
File
::create(
"hello.txt"
);
fs::write(
"hello.txt"
,
"I am hello.txt"
);
}
_
=
> {
/
/
其他原因直接抛 panic
panic!(
"Failed to open the file."
);
}
}
}
}
/
/
打印 txt内容
let mut
file
=
std::fs::
File
::
open
(
"hello.txt"
).unwrap();
let mut contents
=
String::new();
file
.read_to_string(&mut contents).unwrap();
print
!(
"txt 的内容是:{}"
, contents);
}
|
以上代码输出:
1
2
3
4
|
Hello, world!
Failed to
open
the
file
.
File
Not Found. soon create .
txt 的内容是:I am hello.txt
|
泛型这种编程语言得设计绝非 rust 独有的,实际上在C# , Java 这些语言中早就引入了泛型的设计思想。泛型即类型的泛化,在编写代码的时候并不知道具体的数据类型或者数据结构是什么样子的,而是定义一个标识,在运行时此标识可动态替换为实际的数据类型。很显然这种设计能够让开发人员避免编写很多相似的重复的代码。
在 rust 中,可以在如下位置编写泛型代码:
示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
fn main() {
println!(
"Hello, world!-->泛型"
);
/
*
*
泛型函数
*
/
let a
=
[
2
,
4
,
6
,
3
,
1
];
println!(
"数字数组中最大元素是 = {}"
,
max
(&a));
let b
=
[
"A"
,
"B"
,
"C"
];
println!(
"字符数组中最大元素是 = {}"
,
max
(&b));
/
*
*
泛型结构体
*
/
/
/
i32
let p1
=
Point { x:
1
, y:
2
};
/
/
f64
let p2
=
Point { x:
1.0
, y:
2.0
};
println!(
"p1 ={:?} , p2 = {:?}"
, p1, p2);
}
/
/
求最大元素
fn
max
<T: std::
cmp
::PartialOrd>(array: &[T])
-
> &T {
let mut max_index
=
0
;
let mut i
=
1
;
while
i < array.
len
() {
if
array[i] > array[max_index] {
max_index
=
i;
}
i
+
=
1
;
}
return
&array[max_index];
}
/
/
泛型结构体
#[derive(Debug)]
struct Point<T> {
x: T,
y: T,
}
|
以上代码输出:
1
2
3
4
|
Hello, world!
-
-
>泛型
数字数组中最大元素是
=
6
字符数组中最大元素是
=
C
p1
=
Point { x:
1
, y:
2
} , p2
=
Point { x:
1.0
, y:
2.0
}
|
特性(trait)概念接近于 Java 中的接口(Interface),但两者不完全相同。特性与接口相同的地方在于它们都是一种行为规范,可以用于标识哪些类有哪些方法
一个对象的行为由其可供调用的方法构成。如果可以对不同类型调用相同的方法的话,这些类型就可以共享相同的行为了。trait 定义是一种将方法签名组合起来的方法,目的是定义一个实现某些目的所必需的行为的集合。
示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
use std::time::{SystemTime, UNIX_EPOCH};
fn main() {
println!(
"Hello, world!--->特性(接口)"
);
/
/
实例化Person
let p
=
Person {
name: String::
from
(
"cml"
),
age:
100
,
};
let d
=
p.describe();
println!(
"d = {}"
, d);
let s
=
p.tostring();
println!(
"s={}"
, s);
let t
=
p.nowTime();
println!(
"t ={:?}"
, t);
}
/
/
定义一个描述接口
trait Descriptive {
fn describe(&
self
)
-
> String;
/
/
接口默认方法
fn nowTime(&
self
)
-
> SystemTime {
let start
=
SystemTime::now();
return
start;
}
}
/
/
定义一个 tostring 接口
trait ToString {
fn tostring(&
self
)
-
> String;
}
/
/
定义一个结构体
#[derive(Debug)]
struct Person {
name: String,
age: u8,
}
/
/
结构体的一个继承类实现了 Descriptive 接口
impl Descriptive
for
Person {
fn describe(&
self
)
-
> String {
format
!(
"I am Person , name is : {} , age is : {}"
,
self
.name,
self
.age
)
}
}
impl ToString
for
Person {
fn tostring(&
self
)
-
> String {
format
!(
"Person:{{name:{},age:{}}}"
,
self
.name,
self
.age)
}
}
|
以上代码输出:
1
2
3
4
|
Hello, world!
-
-
-
>特性(接口)
d
=
I am Person , name
is
: cml , age
is
:
100
s
=
Person:{name:cml,age:
100
}
t
=
SystemTime { intervals:
132914629768805244
}
|
rust 标准库中的 std::fs 可以用来操作文件
示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
use std::fs;
use std::fs::OpenOptions;
use std::io::prelude::
*
;
fn main() {
println!(
"Hello, world!--->文件io"
);
/
/
读取文件内容,一次性读取
/
/
文件内容:This
is
a text
file
.
let text
=
fs::read_to_string(
"text.txt"
).unwrap();
println!(
"txt文件的内容是:{}"
, text);
/
/
写入文件,追加
/
/
追加完成文件内容:
append();
println!(
"追加后,txt文件的内容是:{}"
, text);
/
/
写入文件,会覆盖
/
/
覆盖之后文件内容:
fs::write(
"text.txt"
,
"FROM RUST PROGRAM"
).unwrap();
println!(
"覆盖写入后,txt文件的内容是:{}"
, text);
}
/
/
追加
fn append()
-
> std::io::Result<()> {
let mut
file
=
OpenOptions::new().append(true).
open
(
"text.txt"
)?;
file
.write(b
" APPEND WORD"
)?;
Ok(())
}
|
以上代码输出:
1
2
3
4
|
Hello, world!
-
-
-
>文件io
txt文件的内容是:This
is
a text
file
.
追加后,txt文件的内容是:This
is
a text
file
.
覆盖写入后,txt文件的内容是:This
is
a text
file
.
|
面向对象编程(OOP)思想是一个概念,是一种思想指导,围绕“对象”展开,从万物皆是对象的角度出发,一个 crate , 一个 module ,一个 struct ,一个枚举等等都是一个个独立的对象,能自主表达一个事物、某种特征。而运用封装、继承、多态等手段可以让对象与对象之间产生某种联系,进而表达更多的事物,解决更多的问题。
面向对象思想是构建大型应用软件系统的基石。
rust 语言中 可以通过 结构体,枚举,特性(trait)等来实现oop思想。
rust 中的并发编程主要得益与 线程 、 消息传递和互斥锁。
Rust 语言是满足多线程特性的,所以 rust 可以满足 主-->子 多任务应用场景。
消息传递有点类似与消息队列,但消息队列一般跨进程或线程,而 rust 中的消息传递主要是主子线程中数据的传递。
示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
use std::thread;
use std::time::Duration;
fn main() {
println!(
"[主线程] Hello, world!--->线程"
);
/
/
创建子线程
for
item
in
0.
.
5
{
println!(
"[主线程] 即将创建一个子线程,当前循环变量:{}"
, item);
let child_thread
=
thread::spawn(|| {
for
i
in
0.
.
2
{
println!(
"[子线程] hi number {} from the spawned thread!"
, i);
thread::sleep(Duration::from_millis(
1
));
}
});
}
for
i
in
0.
.
3
{
println!(
"[主线程] hi number {} from the main thread!"
, i);
thread::sleep(Duration::from_millis(
1
));
}
/
/
线程join
let child1
=
thread::spawn(|| {
println!(
"[child-1]hi I am spawned thread!"
);
thread::sleep(Duration::from_millis(
1
));
});
let child2
=
thread::spawn(|| {
println!(
"[child-2]hi I am spawned thread!"
);
thread::sleep(Duration::from_millis(
1
));
});
child1.join().unwrap();
child2.join().unwrap();
println!(
"[main]hi I am main thread!"
);
}
|
以上代码输出:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
[主线程] Hello, world!
-
-
-
>线程
[主线程] 即将创建一个子线程,当前循环变量:
0
[主线程] 即将创建一个子线程,当前循环变量:
1
[子线程] hi number
0
from
the spawned thread!
[主线程] 即将创建一个子线程,当前循环变量:
2
[子线程] hi number
0
from
the spawned thread!
[主线程] 即将创建一个子线程,当前循环变量:
3
[子线程] hi number
0
from
the spawned thread!
[主线程] 即将创建一个子线程,当前循环变量:
4
[子线程] hi number
0
from
the spawned thread!
[主线程] hi number
0
from
the main thread!
[子线程] hi number
0
from
the spawned thread!
[子线程] hi number
1
from
the spawned thread!
[子线程] hi number
1
from
the spawned thread!
[子线程] hi number
1
from
the spawned thread!
[子线程] hi number
1
from
the spawned thread!
[主线程] hi number
1
from
the main thread!
[子线程] hi number
1
from
the spawned thread!
[主线程] hi number
2
from
the main thread!
[child
-
1
]hi I am spawned thread!
[child
-
2
]hi I am spawned thread!
[main]hi I am main thread!
|
以下示例演示了子线程获得了主线程的发送者 tx,并调用了它的 send 方法发送数据,然后主线程就通过对应的接收者 rx 接收到了发送的数据。
无法在子线程中发送而在另外一个子线程中接收
示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
use std::sync::mpsc;
use std::thread;
fn main() {
println!(
"Hello, world!--->消息传递"
);
let p1
=
Person {
active: true,
name: String::
from
(
"cml"
),
email: String::
from
(
"cnaylor@163.com"
),
sign_in_count:
99
,
tuple
: (
8
,
8
),
};
let (tx, rx)
=
mpsc::channel();
thread::spawn(move || {
tx.send(p1).unwrap();
});
let received
=
rx.recv().unwrap();
println!(
"Got: {:?}"
, received);
}
#[derive(Debug)]
struct Person {
active:
bool
,
name: String,
email: String,
sign_in_count: u64,
tuple
: (u32, u32),
}
|
以上代码输出:
1
2
|
Hello, world!
-
-
-
>消息传递
Got: Person { active: true, name:
"cml"
, email:
"cnaylor@163.com"
, sign_in_count:
99
,
tuple
: (
8
,
8
) }
|
互斥锁(mutex)是 mutual exclusion 的缩写,也就是说,任意时刻,其只允许一个线程访问某些数据。为了访问互斥器中的数据,线程首先需要通过获取互斥器的 锁(lock)来表明其希望访问数据。锁是一个作为互斥器一部分的数据结构,它记录谁有数据的排他访问权。因此,我们描述互斥器为通过锁系统 保护(guarding)其数据。
以下示例演示了在主线程中创建一个值,并在多个子线程中修改此值,最终等所有子线程处理完毕,主线程打印最终值。为了让变量能够跨线程之间共享,引入了std::sync::Arc 这个结构体。
示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
println!(
"Hello, world!--->互斥锁"
);
/
/
Arc 原子引用计数器,确保在多个线程中共享数据;counter初始值为
0
let counter
=
Arc::new(Mutex::new(
0
));
println!(
"counter 初始值为:0"
);
/
/
定义一个子线程集合
let mut handles
=
vec![];
for
_
in
0.
.
10
{
/
/
将counter值从主线程中克隆,并赋值给私有变量 counter
let counter
=
Arc::clone(&counter);
let handle
=
thread::spawn(move || {
/
/
获取互斥锁,并将counter值加一
let mut num
=
counter.lock().unwrap();
*
num
+
=
1
;
let thread_id
=
thread::current().
id
();
println!(
"[子线程:{:?}]中将 counter 的值修改为:{}"
, thread_id, num);
});
/
/
将创建的子线程存储到子线程集合中
handles.push(handle);
}
/
/
将所有的子线程join到主线程,确保主线程等待所有子线程执行成功
for
handle
in
handles {
handle.join().unwrap();
}
let result
=
*
counter.lock().unwrap();
println!(
"Result: {}"
, result);
}
|
以上代码输出:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
Hello, world!
-
-
-
>互斥锁
counter 初始值为:
0
[子线程:ThreadId(
2
)]中将 counter 的值修改为:
1
[子线程:ThreadId(
4
)]中将 counter 的值修改为:
2
[子线程:ThreadId(
3
)]中将 counter 的值修改为:
3
[子线程:ThreadId(
5
)]中将 counter 的值修改为:
4
[子线程:ThreadId(
8
)]中将 counter 的值修改为:
5
[子线程:ThreadId(
6
)]中将 counter 的值修改为:
6
[子线程:ThreadId(
7
)]中将 counter 的值修改为:
7
[子线程:ThreadId(
9
)]中将 counter 的值修改为:
8
[子线程:ThreadId(
10
)]中将 counter 的值修改为:
9
[子线程:ThreadId(
11
)]中将 counter 的值修改为:
10
Result:
10
|
本文示例代码维护在gitee上面:https://gitee.com/naylor_personal/rust-hello-world
打开代码仓库中的 Sport 文件夹,有惊喜!!!
更多【Rust有人玩吗?编写了一个入门文档,分享一波】相关视频教程:www.yxfzedu.com