什么是trait,trait相对于java就是interface。
基本的trait例子
pub trait Draw {
fn draw(&mut self);
}
struct Triangle {
a: u32,
b: u32,
c: u32,
}
impl Draw for Triangle {
fn draw(&mut self) {
println!("{} {} {}", self.a, self.b, self.c);
}
}
fn main() {
let mut t = Triangle{a: 1, b: 2, c: 3};
t.draw();
}
在trait中,由self和Self,其中self是self: Self的简写。这里self可以和python比对,self代表的是实现trait的数据结构本身。
trait关联类型
关联类型是一种占位符,只有在impl的时候才会具体指定关联类型
pub trait Draw {
type Output;
fn draw(&mut self) -> Self::Output;
}
struct Triangle {
a: u32,
b: u32,
c: u32,
}
impl Draw for Triangle {
type Output = String;
fn draw(&mut self) -> Self::Output{
println!("{} {} {}", self.a, self.b, self.c);
"ok".to_string();
}
}
fn main() {
let mut t = Triangle{a: 1, b: 2, c: 3};
let r = t.draw();
println!("{}", r)
}
带有泛型的trait
rust也支持泛型, 泛型是C++和JAVA语言的语言范式。
pub trait Add {
type Output;
#[must_use]
fn add(self, rhs: Rhs) -> Self::Output;
}
impl Add for i32 {
type Output = i32;
fn add(self, rhs: i32) -> i32 {
return self + rhs
}
}
impl Add for u32 {
type Output = i32;
fn add(self, rhs: u32) -> i32 {
return (self + rhs) as i32
}
}
fn main() {
let (a, b, c, d) = (1i32, 2i32, 3u32, 4u32);
let x: i32 = a.add(b);
let y: i32 = c.add(d);
assert_eq!(x, 3i32);
assert_eq!(y, 7i32);
}
面向对象
上一篇文章函数里面举到了例子
#[derive(Debug, PartialEq)]
struct Foo(i32);
#[derive(Debug, PartialEq)]
struct Bar(i32, i32);
trait Inst {
fn new(i: i32) -> Self;
}
impl Inst for Foo {
fn new(i: i32) -> Foo {
Foo(i)
}
}
impl Inst for Bar {
fn new(i: i32) -> Bar {
Bar(i, i + 10)
}
}
fn foobar(i: i32) -> T {
T::new(i)
}
fn main() {
let f: Foo = foobar1(10);
println!("{:?}", f);
let b: Bar = foobar1(10);
println!("{:?}", b);
}
trait object 实现动态分派
当我们想像java语言一样,在Vec中装载很多实现同一trait的对象的时候,就需要用到&dyn
#[derive(Debug, PartialEq)]
struct Foo(i32);
#[derive(Debug, PartialEq)]
struct Bar(i32, i32);
trait Format {
fn format(&self);
}
impl Format for Foo {
fn format(&self) {
println!("{:?}", self)
}
}
impl Format for Bar {
fn format(&self){
println!("{:?}", self)
}
}
fn format_vec(fs : Vec<&dyn Format>) {
for f in fs {
f.format();
}
}
fn main() {
let f: &dyn Format = &Foo(10);
let b: &dyn Format = &Bar(10, 10);
let vecs = vec![f, b];
format_vec(vecs);
}
trait object实际上是一个胖指针,例如:
这个胖指针一边指向trait,一边指向虚函数表,这和C++/JAVA多态底层的原理是一样的。
小结
trait是rust语言的精华,trait中包含了面向对象编程、泛型编程范式,同时也包含了C++/JAVA语言多态原理,掌握trait是掌握rust语言范式的关键。