说我有以下特质:
trait SayHello {
fn say_hello(self) -> String;
}
为什么我可以这样做:
fn say_hello_twice(this: impl SayHello) -> String {
let said_hello = this.say_hello();
said_hello + " " + &said_hello
}
但不是这个:
impl impl SayHello {
fn say_hello_twice(self) -> String {
let said_hello = self.say_hello();
said_hello + " " + &said_hello
}
}
当后者看似只是前者的语法糖时?
impl Trait
不是类型:从语义上讲,它的意思是“碰巧实现 Trait
的某种具体类型”。 在参数位置,就像在原来的 say_hello_twice
函数中一样,它只是泛型的语法糖 - 脱糖后的等价物如下:
fn say_hello_twice<T: SayHello>(this: T) -> String { ... }
它在 Rust 中是不允许的,所以它实际上没有任何定义的含义,但是使用
impl Trait
作为实现块的名义类型可能会有什么意图? 类比上面,或许是这样的:
impl<T: SayHello> T { ... }
但是,这也是不允许的:这样的实现将适用于实现
SayHello
的所有类型:包括在下游 crate 中定义的任何类型,并且固有实现只能在与类型本身相同的 crate 中定义,以防止名称冲突/歧义。
如果您想定义一个可用于
SayHello
的所有实现的方法,您有几种选择:
在
SayHello
特征中定义它,并使用默认实现:
trait SayHello {
fn say_hello(self) -> String;
fn say_hello_twice(self) -> String {
let said_hello = self.say_hello();
said_hello.clone() + " " + &said_hello
}
}
这有一个优点,那就是它只是
SayHello
上常见的、现成的方法。 然而,实现者可以选择覆盖定义——这可能是理想的,也可能是不理想的。
定义一个全面实现的扩展特征:
trait SayHelloTwice {
fn say_hello_twice(self) -> String;
}
impl<T: SayHello> SayHelloTwice for T {
fn say_hello_twice(self) -> String {
let said_hello = self.say_hello();
said_hello.clone() + " " + &said_hello
}
}
这样做的优点是
SayHello
的实现不能提供SayHelloTwice::say_hello_twice
的任何其他定义。 但是,用户必须将 SayHelloTwice
纳入范围内,这稍微不太符合人体工程学。
可以使用
dyn Trait
作为固有实现块的标称类型:
impl dyn SayHello {
fn say_hello_twice(&self) -> String {
let said_hello = self.say_hello_borrowed();
said_hello.clone() + " " + &said_hello
}
}
但是,用户必须使用特征对象(
&dyn SayHello
、Box<dyn SayHello>
等),这涉及运行时间接,可能不是您想要的;此外,由于 dyn Trait
未调整大小,因此您无法按值获取 self
(因此使用上面的 &self
和 say_hello_borrowed
)。