trait

Trait(特征)

derive(起源)

编译器可以使用#[derive]属性提供一些trait的基本实现。声明属性后的trait实现,依然可以自己重新方法。

The following is a list of derivable traits:

  • Comparison traits: Eq, PartialEq, Ord, PartialOrd.
  • Clone, to create T from &T via a copy.
  • Copy, to give a type ‘copy semantics’ instead of ‘move semantics’.
  • Hash, to compute a hash from &T.
  • Default, to create an empty instance of a data type.
  • Debug, to format a value using the {:?} formatter.

returning traits with dyn

编译器需要明确的知道返回值需要占用多少字节的空间,所以每一个函数的返回类型必须是具体类型。
当需要返回一个trait的具体实现类时,由于不同实现可能占用不同大小的空间,所以必能直接使用trait作为返回值。

我们可以通过返回一个Box,来绕过这样的限制。由于box是指向heap中的一块区域的引用,所以它拥有固定的长度。
rust总是试图明确的确定要在heap上分配多大内存,所以,如果你试图返回一个point-to-trait-on-heap的值时,需要通过dyn关键字。

1
2
3
4
5
6
7
fn random_animal(random_number: f64) -> Box<dyn Animal> {
if random_number < 0.5 {
Box::new(Sheep {})
} else {
Box::new(Cow {})
}
}

操作符重载

rust中操作符重载,非常简单,只需要实现具体的trait就可以了。
rust中的操作符相关的trait都在core::ops包中。

Drop

Drop trait只有一个drop方法,它将在对象作用域结束的时候被自动调用。

当需要返回一个类型时返回 impl Trait

impl可以用来返回trait和闭包类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fn combine_vecs(
v: Vec<i32>,
u: Vec<i32>,
) -> impl Iterator<Item=i32> {
v.into_iter().chain(u.into_iter()).cycle()
}

fn main() {
let v1 = vec![1, 2, 3];
let v2 = vec![4, 5];
let mut v3 = combine_vecs(v1, v2);
assert_eq!(Some(1), v3.next());
assert_eq!(Some(2), v3.next());
assert_eq!(Some(3), v3.next());
assert_eq!(Some(4), v3.next());
assert_eq!(Some(5), v3.next());
println!("all done");
}
1
2
3
4
5
// Returns a function that adds `y` to its input
fn make_adder_function(y: i32) -> impl Fn(i32) -> i32 {
let closure = move |x: i32| { x + y };
closure
}
  • 函数声明的返回值
    -> Box
    -> impl Trait
    有啥区别?
    动态分发和静态分发

Clone Trait

当我们赋值或者调用函数时,默认是会发生所有权转移。有时候我们并不想让所有权发生转移,那么我们可以实现Clone Trait。

super trait

rust中并没有继承的概念,但是,我们可以定义一个trait是其他trait的超集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
trait Person {
fn name(&self) -> String;
}

// Student is a supertrait of Person.
// Implementing Student requires you to also impl Person.
trait Student: Person {
fn university(&self) -> String;
}

trait Programmer {
fn fav_language(&self) -> String;
}

// CompSciStudent (computer science student) is a supertrait of both Programmer
// and Student. Implementing CompSciStudent requires you to impl both subtraits.
trait CompSciStudent: Programmer + Student {
fn git_username(&self) -> String;
}

消除重叠trait中的“二义性”

我们可能实现多个trait,这些trait中可能会有重复的方法,怎么去消除“二义性”

  1. 由于对不同trait的实现是在不同的impl块中,所以,很容易对重复的方法实现。
  2. 当调用重复的方法时,需要用as关键字明确的指定要调用那个trait中的方法。
    1
    2
    3
    4
    let username = <Form as UsernameWidget>::get(&form);
    assert_eq!("rustacean".to_owned(), username);
    let age = <Form as AgeWidget>::get(&form);
    assert_eq!(28, age);
,
© 2023 PLAYAROUND All Rights Reserved. 本站访客数人次 本站总访问量
Theme by hiero