魔法师の高塔

Rust学习 1

看了Rust的语法和它的特色之处——所有权系统,感觉上概念已经理解,但没有长期的Rust编程训练的话,也只能,仅仅是知道而已。

Hello World

1
2
3
fn main(){
println!("Hello world");
}

除了fn外,似乎Rust的”Hello World”与C没有太大区别。具体来说,fn是Rust中函数的关键字,而关于类型说明,()是可以省略的。

1
2
3
fn main() -> (){
println!("Hello World");
}

所有权

所有权系统是Rust语言中最基本最独特也是最重要的特性之一,它号称只要通过编译就不会发生崩溃,这样的优点就是基于Rust的所有权系统。
所有权系统包括三部分:

  • Ownership 所有权
  • Borrowing 借用
  • Lifetimes 生命周期

绑定

准确地说Rust中变量这个概念应该称为标识符,资源绑定到这个标识符上。
Rust明确规定变量的初始值必须由程序员自己制定。

1
2
3
let a : i32;
a = 100;
println!("{}", a);

let关键字并不只是声明变量的意思,他还有一层重要的概念,绑定。在上面的程序中,let声明了一个变量,而在内存区,栈内存上分配了一个i32的资源,并填充值100,随后,将这个资源和标识符做绑定,a就成了资源的所有者。

可变性

修改一个变量的值,在Rust中是必须要显式声明的。
let mut a=10;a=20;

作用域

Rust通过{}定义作用域。在局部变量离开作用域时,Rust将会把资源与变量一起销毁释放。

移动语义

在Rust中,与绑定这个概念相辅相成的另一个机制便是『移动所有权』,可以把资源的所有权从一个绑定移动成另一个绑定,这个操作同样通过关键字let完成。
在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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
use std::fmt;
#[derive(Debug)]
enum List<T> {
Nil,
Cons(T,Box<List<T>>),
}
macro_rules! list {
() => (List::Nil);
($x:expr) => (List::Cons($x,Box::new(List::Nil)));
($x:expr,$($y:expr),*) => ({
List::Cons($x,Box::new(list!($($y),*)))
});
}
impl<T> List<T> {
fn map<B, F>(&self, f: F) -> List<B>
where F: Fn(&T) -> B
{
match *self {
List::Nil => List::Nil,
List::Cons(ref h, ref t) => List::Cons(f(h), Box::new(t.map(f))),
}
}
}
trait Monoid {
fn zero(&self) ->Self;
fn append(&self,x: Self) -> Self;
}
impl Monoid for i32 {
fn zero(&self) ->i32{
1
}
fn append(&self,x: i32) -> i32{
self * x
}
}
fn foldr<T>(x: List<T>) -> Option<T>
where T : fmt::Debug + fmt::Display + Monoid
{
match x {
List::Nil => None,
List::Cons(head,tail) => {
let tail = match foldr(*tail) {
None => head.zero(),
Some(v) => v,
};
Some(head.append(tail))
}
}
}
fn main() {
let a : List<i32> = list!(1,2,3,4);
println!("{:?}", foldr(a));
}

看完这段代码,很显然可以感觉到Rust与Haskell的一些概念极为类似,这些概念也是一个现代语言都基本实现的东西,诸如代数数据结构,泛型,Trait,模式匹配,#[derive(some)],更妙的是Rust的宏比C/C++更强大。
在上面的Demo中,main中的a带入foldr,这里a的所有权已经转移到foldr里,所以如果在main结尾添上一句println!("{:?}",a);,编译通不过。
如何解决这个问题?

借用&引用

借用并不会发生所有权的移动,将foldr函数修改为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
fn foldr<T>(x: & List<T>) -> Option<T>
where T : fmt::Debug + fmt::Display + Monoid
{
match *x {
List::Nil => None,
List::Cons(ref head,ref tail) => {
let tail = match foldr(tail) {
None => head.zero(),
Some(v) => v,
};
Some(head.append(tail))
}
}
}
fn main() {
let a : List<i32> = list!(1,2,3,4);
println!("{:?}", foldr(&a));
println!("{:?}", a);
}