Generics(泛型)

Generics是Rust中的重要内容,能够最大化地减少代码的重复。fn foo<T>(arg: T) { ... }来定义一个函数,传入的参数为T

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
struct Val {
    val: f64,
}
impl Val {
    pub fn value(&self) -> &f64 {
        &self.val
    }
}
fn main() {
    let x = Val { val: 3. };
    let y = x.value();
}

trait(接口)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
//定义接口
trait HasArea {
    fn area(&self) -> f64;
}

struct Rectangle { length: f64, height: f64 }

// 实现接口
impl HasArea for Rectangle {
    fn area(&self) -> f64 {
        self.length * self.height
    }
}

//方法
fn area<T: HasArea>(t: &T) -> f64 { t.area() }

fn main() {
    let r = Rectangle { length: 5., height: 4. };
    let x = r.area();
    println!("{}", x);
}

如果没有实现接口,就会出现错误。在java中可以实现多个接口,那么rust中也是可以的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
use std::fmt::{Debug, Display, Formatter, Error, Result};
fn compare_prints<T: Debug + Display>(t: &T) {
    println!("Debug: `{:?}`", t);
    println!("Display: `{}`", t);
}
fn main() {
    let string = "words";
    let array = [1, 2, 3];
    compare_prints(&string);
}

where(限定符)

 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::Debug;

trait PrintInOption {
    fn print_in_option(self);
}

// Because we would otherwise have to express this as `T: Debug` or 
// use another method of indirect approach, this requires a `where` clause:
impl<T> PrintInOption for T where
    Option<T>: Debug {
    // We want `Option<T>: Debug` as our bound because that is what's
    // being printed. Doing otherwise would be using the wrong bound.
    fn print_in_option(self) {
        println!("{:?}", Some(self));
    }
}

fn main() {
    let vec = vec![1, 2, 3];

    vec.print_in_option();
}

作用域规则

在Rust语言中,作用域规则非常重要,这也是Rust语言的垃圾回收的重要机制。Rust对变量的处理不仅仅是只分配空间,同时掌握着变量的生存周期,一旦一个变量离开了上下文,那么就使用垃圾回收将其回收。RAIIRAII-wiki内存泄露工具可以使用valgrind

在Rust中,资源只能有一个所有者,不仅仅能够保证资源不会泄露,同时保证了资源不会被释放两次。当资源的所有者被转移以后,之前的所有者就不存在了,这样也就避免了悬挂指针的出现。

在Rust中,有堆和栈的区别。堆分配非常慢,而栈非常快,使用Box::new()则是在堆中分配。

1
2
3
let x = Box::new(4i32);
let y = x;
print!("{}", x);

在这里,x就没有用,因为所有权已经被转移到y上。

借用(Borrowing)

很多时候我们不想取得变量的所有权,我们只想得到变量的值,那么这个时候就采用借用,使用&T

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
fn eat_box_i32(boxed_i32: Box<i32>) {
    println!("Destroying box that contains {}", boxed_i32);
}
fn borrow_i32(borrowed_i32: &i32) {
    println!("This int is: {}", borrowed_i32);
}
fn main() {
    let boxed_i32 = Box::new(5_i32);
    let stacked_i32 = 6_i32;

    borrow_i32(&boxed_i32);

    println!("{}", boxed_i32);
}

//下面两个是一样的
let ref ref_c1 = c;
let ref_c2 = &c;

生命周期(lifetime)

1
2
3
4
5
6
7
8
struct Object {
    number: u32
}

struct Multiplier {
    object: &Object,
    mult: u32
}
  • 首先定义了一个结构体Object,然后里面有一个内容number,这没有问题。
  • 其次定义了Multiplier,然后里面有mult,以及object。 这里就出现问题了,object可能生存任何期间,也就是比Multiplier生命周期短!这就是问题, Rust是这样定义生命周期
1
2
3
4
struct Multiplier<'a> {
    object: &'a Object,
    mult: u32
}

有一个结构体生命周期为’a,同时有一个object的生命周期至少与’a一样长

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
struct Object {
    number: i32,
}

fn object_combinator<'a, 'b>(a: &'a mut Object, b: &'b Object) -> &'a mut Object {
    a.number = a.number + b.number;
    a
}

fn main() {
    let mut a = Object { number: 3 };
    let b = Object { number: 4 };
    println!("Result: {}", object_combinator(&mut a, &b).number);
}

//意味着该变量存在与整个程序
let string: &'static str = "I'm a string! Yay!";

参考