《RustPrimer》在线阅读链接:https://rustcc.gitbooks.io/rustprimer/content/
Rust是一门系统级编程语言,被设计为保证内存和线程安全,并防止段错误。它的基本理念是“零开销抽象”。理论上来说,它的速度与C/C++同级。
Rust使用实现(implementation)、特征(trait)和结构化类型(structured type)而不是类(class)。就这一点而言,与基于继承的OO语言C++、Java有相当大的差异。而跟Haskell这类函数式语言更加接近。
Rust做到了内存安全而无需.NET和Java编程语言中实现自动垃圾收集器的开销,这是通过所有权/借用机制、生命周期、以及类型系统来达到的。
影响了Rust的流行编程语言包括C,C++,C#,Erlang,Haskell,OCaml,Ruby,Scheme和Swift等等。Rust也影响了C# 7,Elm,Idris,Swift。
Rust简史。Rust最早是Mozilla雇员Graydon Hoare的个人项目,从2009年开始,得到了Mozilla研究院的支助,2010年项目对外公布。2010~2011年间实现自举(用rust写rust)。从此以后,Rust经历了巨大的设计变化和反复(历程及其艰辛),终于在2015年5月15日发布了1.0版。Rust现在由Rust项目开发社区(https://github.com/rust-lang/rust)维护。
自15年5月1.0发布以来,涌现了大量优秀的项目(可以在github上搜索Rust查找),大公司也逐渐积极参与Rust的应用开发,以及回馈开源社区。
【注】Rust在众多区块链项目中被作为主要开发语言。比较有代表性的有:以太坊EVM实现parity-ethereum,比特币的Rust实现parity-bitcoin,cardano项目钱包和密码模块的Rust实现:rust-cardano等等。
Rust支持主流的OS,包括Linux,Mac和Windows。
Rust团队为Linux用户提供了两种安装方式:
1、直接下载安装包
2、命令行一键安装
【注意】如果你不想在电脑上安装Rust,但又想尝试一下Rust,推荐一个在线环境:http://play.rust-lang.org/
Rust团队为Mac用户提供了两种安装方式:
1、直接下载安装包
2、命令行一键安装
由于官网提供了很好的安装脚本或者Rust的安装包。这里就不展开。
1、Vim
2、vscode
helloworld.rs
fn main(){
println!("Hello World!");
}
编译helloworld.rs文件
> rustc helloworld.rs
> rustc helloworld.rs -O # 编译优化
Hello World!
让我们来解读一下这个小程序。
1、第一行fn
表示一个函数,main
是这个函数的名字,花括号{}
里的语句则表示这个函数的内容。
2、名字叫做main的函数有特殊的用途,那就是程序的入口,也就是说程序每次都是从这个函数开始运行。
3、函数中只有一句println!("Hello World!");
,这里println!
是一个Rust语言自带的宏,这个宏的功能就是打印文本(结尾会换行)。
4、在Rust语言中,分号;
用来把语句分隔开,也就是说语句的末尾一般用分号作为结束标志。
现在我们修改一下helloworld.rs程序,改为:
fn main(){
let rust = "Rust";
println!("Hello, {}!",rust);
}
这里let rust = "Rust";
是把rust
变量绑定为"Rust"
,println!("Hello, {}!",rust);
这条语句把rust变量的值代入到"Hello, {}!"
中的{}
。
变量绑定
Rust通过let
关键字进行变量绑定【let有点像数学中的令一个变量】。
main1.rs
fn main(){
let a1 =5; //Rust默认类型推到是i32
let a2:i32 =5;
assert_eq!(a1,a2);
let b1:u32=5;
assert_eq!(a1,b1);
}
编译main.rs(rustc main.rs),报下面的错误:
error[E0308]: mismatched types
--> main.rs:11:2
|
11 | assert_eq!(a1,b1);
| ^^^^^^^^^^^^^^^^^^ expected i32, found u32
|
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error: aborting due to previous error
这里的assert_eq!
宏的作用是判断两个参数是不是相等的,但如果是两个不匹配的类型,就算字面值相等也会报错。
可变绑定
rust在声明变量时,在变量前面加入mut关键字,变量就会成为可变绑定的变量。
main2.rs
fn main(){
let mut a:f64 =1.0;
let b =2.0f32;
a = 2.0;
println!("{:?}",a);
let a =a;
a=3.0;
assert_eq!(a,b);
}
编译main2.rs,报下面的错误:
error[E0308]: mismatched types
--> main.rs:16:2
|
16 | assert_eq!(a,b);
| ^^^^^^^^^^^^^^^^ expected f64, found f32
|
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
help: you can cast an `f32` to `f64` in a lossless way
|
5 | if ! ( * left_val == (* right_val).into() ) {
| ^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
这里的b变量,绑定了2.0f32。这是Rust里面值类型显式标记的语法,规定value
+type
的形式。
例如:固定大小类型
1u8 1i8
1u16 1i16
1u32 1i32
1u64 1i64
可变大小类型
1usize 1isize
浮点类型
1f32 1f64
let解构
为什么在Rust里面声明一个变量的时候要采用let绑定表达式?那是因为let绑定表达式的表达能力更强,而且let表达式实际上是一种模式匹配。
例如:
fn main(){
let (a,mut b):(bool,bool)=(true,false);
println!("a={:?},b={:?}",a,b);
//a=false;
b=true;
assert_eq!(a,b);
}
报错:
error[E0384]: cannot assign twice to immutable variable `a`
--> main.rs:11:2
|
6 | let (a,mut b):(bool,bool)=(true,false);
| - first assignment to `a`
...
11 | a=false;
| ^^^^^^^ cannot assign twice to immutable variable
error: aborting due to previous error
原生类型
Rust支持的原始数据类型(primitive types)有以下几类:
i6
,i16
,i32
,i64
,isize
)、无符号整数(u6
,u16
,u32
,u64
,usize
)以及浮点数(f32
,f64
)。str
,更常用的是字符串切片&str
和堆分配字符串String
,其中字符串切片是静态分配的,有固定的大小,并且不可变,而堆分配字符串是可变的。*const T
和*mut T
,但解引用它们是不安全的,必须放到unsafe
块里。 ()
,其唯一的值也是()
。例子:
fn main(){
//boolean type
let t = true;
let f:bool = false;
//char type
let c = 'c';
//numeric types
let x =42;
let y: u32 = 123_456;
let z: f64 = 1.23e+2;
let zero =z.abs_sub(123.4);
let bin = 0b1111_0000;
let oct = 0o7320_1546;
let hex = 0x723a_b049;
//string types
let str = "Hello, world!";
let mut string = str.to_string();
//arrays and slices
let a = [0,1,2,3,4];
let middle = &[1..4];
let mut ten_zeroes:[i64;10]:[0;10]
//tuples
let tuple: (i32,&str) = (50,"hello");
let (fifty,_) = tuple;
let hello = tuple.1;
//raw pointer
let x =5;
let raw = &x as *const i32;
let points_at = unsafe{*raw};
//function
fn foo(x:i32) -> i32 {x}
let bar: fn(i32)->i32=foo;
}
有几点需要特别注意的:
_
分隔符来增加可读性。b'H'
以及单字符串b”Hello”,仅限制于ASCII字符。此外,还可以使用r#"..."#
标记来表示原始字符串,不需要对特殊字符进行转义。&
符号将String
类型转换成&str
类型很廉价,但是使用to_string()
转换到String
类型涉及到分配内存,除非很有必要否则不要这么做。vec!
创建。==
和!=
运算符来判断是否相同。as
关键字显式转换。-可以使用type
关键字定义某个类型的别名,并且应该采用驼峰命名法。
例子:
//显式转换
let decimal = 65.4321_f32;
let integer = decimal as u8;
let character = integer as char;
//type aliases
type NanoSecond = u64;
type Point =(u8,u8);
数组array
Rust使用数组存储相同类型的数据集。[T;N]
表示一个拥有T类型,N个元素的数组。数组的大小是固定的。
//main3.rs
fn main(){
let mut array: [i32;3] = [0;3];
array[1]=1;
array[2]=2;
assert_eq!([1,2],&array[1..]);
for x in &array {
println!("{} ",x);
}
}
先编译(rustc main3.rs),再运行(./main3),打印如下:
0
1
2
我们将上面的程序稍微修改一下,修改如下:
fn main(){
let array:[i32;3]=[0;3];
for x in &array {
println!("{} ",x);
}
}
打印结果是:
0
0
0
动态数组Vec
动态数组是一种基于堆内存申请的连续动态数据类型,拥有O(1)的时间复杂度的索引、压入(push)、弹出(pop)。
例子:
//创建空Vec
let v: Vec = Vec::new();
//使用宏创建空Vec
let v: Vec = vec![];
//创建包含5个元素的Vec
let v = vec![1,2,3,4,5];
//创建十个零
let v = vec![0;10];
//创建可变的Vec,并压入元素3
let mut v = vec![1,2];
v.push(3);
//创建拥有两个元素的Vec,并弹出一个元素
let mut v = vec![1,2];
let two = v.pop();
//创建包含三个元素的可变Vec,并索引1个值和修改一个>值
let mut v = vec![1,2,3];
let three = v[2];
v[1] = v[1]+5;
字符串
Rust里面有两种字符串类型。String
和str
。
&str
str
类型基本上不怎么使用,通常使用&str
类型,它其实是[u8]
类型的切片形式&[u8]
。这是一种固定大小的字符串类型。常见的字符串字面值就是&'static str
类型。这是一种带有'static
生命周期的&str类型。
例子:
//字符串字面量
let hello = "Hello, world!";
//附带显式类型标识
let hello: &'static str = "Hello ,World!";
String
String
是一个带有vec:Vec
成员的结构体,你可以理解为str
类型的动态形式。它们的关系相当于[T]
和Vec
的关系。显然String
类型也有压入和弹出。
例子:
//创建一个空的字符串
let mut s = String::new();
//从`&str`类型转换为`String`类型
let mut hello = String::from("Hello, ");
//压入字符和压入字符串切片
hello.push('w');
hello.push_str("orld!");
//弹出字符
let mut s = String::from("foo");
assert_eq!(s.pop(), Some('o'));
assert_eq!(s.pop(), Some('o'));
assert_eq!(s.pop(), Some('f'));
assert_eq!(s.pop(), None);