rust 没有像其他编程语言一样提供 interface{} 接口,但是提供了类似用于定于行为的 trait。trait 到底是什么?跟函数有什么不一样?
比如定义一个 Animal:
trait Animal {
fn show(&self);
fn grow(&mut self);
fn eat(self);
}
fn main() {}
转为汇编代码的时候, Animal trait 没有生成相关的代码
添加 Fish 实现
trait Animal {
fn show(&self);
fn grow(&mut self);
fn eat(self);
}
struct Fish;
impl Animal for Fish {
fn show(&self) {}
fn grow(&mut self) {}
fn eat(self) {}
}
fn main() {
let mut f = Fish;
f.show();
f.grow();
f.eat();
}
再看生成的汇编代码,可以看到生成了三个在 <playground::Fish as playground::Animal> 下的方法
<playground::Fish as playground::Animal>::show:
movq %rdi, -8(%rsp)
retq
<playground::Fish as playground::Animal>::grow:
movq %rdi, -8(%rsp)
retq
<playground::Fish as playground::Animal>::eat:
retq
playground::main:
pushq %rax
leaq 7(%rsp), %rdi
callq <playground::Fish as playground::Animal>::show
leaq 7(%rsp), %rdi
callq <playground::Fish as playground::Animal>::grow
callq <playground::Fish as playground::Animal>::eat
popq %rax
retq
main:
pushq %rax
movq %rsi, %rdx
movq __rustc_debug_gdb_scripts_section__@GOTPCREL(%rip), %rax
movb (%rax), %al
movslq %edi, %rsi
leaq playground::main(%rip), %rdi
xorl %ecx, %ecx
callq std::rt::lang_start
popq %rcx
retq
从 show 方法的定义如下:
<playground::Fish as playground::Animal>::show:
movq %rdi, -8(%rsp)
retq
show 的方法域为 <playground::Fish as playground::Animal>
调用代码为 <playground::Fish as playground::Animal>::show
trait Animal {
fn show(&self);
fn grow(&mut self);
fn eat(self);
}
struct Fish;
impl Animal for Fish {
fn show(&self) {}
fn grow(&mut self) {}
fn eat(self) {}
}
fn main() {
let mut f = Fish;
f.show();
Animal::show(&f); // 通过 Animal::show 方式调用 方法
f.grow();
f.eat();
}
playground::main:
pushq %rax
leaq 7(%rsp), %rdi
callq <playground::Fish as playground::Animal>::show
leaq 7(%rsp), %rdi
callq <playground::Fish as playground::Animal>::grow
callq <playground::Fish as playground::Animal>::eat
popq %rax
retq
main:
pushq %rax
movq %rsi, %rdx
movq __rustc_debug_gdb_scripts_section__@GOTPCREL(%rip), %rax
movb (%rax), %al
movslq %edi, %rsi
leaq playground::main(%rip), %rdi
xorl %ecx, %ecx
callq std::rt::lang_start
popq %rcx
retq
通过 main 方法的定义还可以知道, 在 rust 中, fn main(){} 只是我们业务定义的 main 方法, 并且 rust 还会生成一个 真正的 main 方法
修改一下 show 的调用方式
trait Animal {
fn show(&self);
fn grow(&mut self);
fn eat(self);
}
struct Fish;
impl Animal for Fish {
fn show(&self) {}
fn grow(&mut self) {}
fn eat(self) {}
}
fn main() {
let mut f = Fish;
f.show();
Animal::show(&f); // 通过 Animal::show 方式调用 方法
<Fish as Animal>::show(&f); // 通过 Fish 来调用 show
Fish::show(&f);
f.grow();
f.eat();
}
playground::main:
pushq %rax
leaq 7(%rsp), %rdi
callq <playground::Fish as playground::Animal>::show
leaq 7(%rsp), %rdi
callq <playground::Fish as playground::Animal>::show
leaq 7(%rsp), %rdi
callq <playground::Fish as playground::Animal>::show
leaq 7(%rsp), %rdi
callq <playground::Fish as playground::Animal>::show
leaq 7(%rsp), %rdi
callq <playground::Fish as playground::Animal>::grow
callq <playground::Fish as playground::Animal>::eat
popq %rax
retq
通过生成的代码可以知道, 这 4 种 show 调用方式生成的代码都是一样的。
trait Animal {
fn show(&self);
fn grow(&mut self);
fn eat(self);
}
struct Fish;
impl Fish {
// 实现 show 方法
fn show(&self) {}
}
impl Animal for Fish {
fn show(&self) {}
fn grow(&mut self) {}
fn eat(self) {}
}
fn main() {
let mut f = Fish;
f.show();
Animal::show(&f); // 通过 Animal::show 方式调用 方法
<Fish as Animal>::show(&f); // 通过 Fish 来调用 show
Fish::show(&f);
f.grow();
f.eat();
}
然后代码的调用方式
# Fish 新增的 show 方法
playground::Fish::show:
movq %rdi, -8(%rsp)
retq
# .... 省略了一部分的代码
playground::main:
pushq %rax
leaq 7(%rsp), %rdi
callq playground::Fish::show
leaq 7(%rsp), %rdi
callq <playground::Fish as playground::Animal>::show
leaq 7(%rsp), %rdi
callq <playground::Fish as playground::Animal>::show
leaq 7(%rsp), %rdi
callq playground::Fish::show
leaq 7(%rsp), %rdi
callq <playground::Fish as playground::Animal>::grow
callq <playground::Fish as playground::Animal>::eat
popq %rax
retq
对比上述的代码, 调用 show 方法不一样了
f.show() => Fish::show
Animal::show(&f) => <playground::Fish as playground::Animal>::show
<Fish as Animal>::show(&f) => <playground::Fish as playground::Animal>::show
Fish::show(&f) => Fish::show
总结
- trait 可以看作是一个定义一些列方法和其他属性的 mod
- 实现一个 trait 就相当于为 类型添加 trait 中定义的方法
- 如果实现多个同名的方法时,优先在当前的struct 作用域下的方法