如何实现特质

How to implement a trait

我正在尝试为 String 实现一个名为 AppendBar 的新特征。它的唯一功能是append_bar.

根据我的理解,self 应该是字符串的一个实例。

trait AppendBar {
    fn append_bar(self) -> Self;
}

impl AppendBar for String {
    fn append_bar(self) -> Self{
        self.clone().push_str("Bar")
    }
}

fn main() {
    let s = String::from("Foo");
    let s = s.append_bar();
    println!("s: {}", s);  // "s: FooBar"
}

这显然不是这种情况,因为我收到以下错误:

error[E0308]: mismatched types
  --> exercises/traits/traits1.rs:18:9
   |
17 |     fn append_bar(self) -> Self{
   |                            ---- expected `std::string::String` because of return type
18 |         self.clone().push_str("Bar")
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::string::String`, found `()`

谁能帮我理解我的误解?

17 |     fn append_bar(self) -> Self{
   |                            ---- expected `std::string::String` because of return type
18 |         self.clone().push_str("Bar")
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::string::String`, found `()`

是说它期望 append_bar 到 return 一个 String,但是 self.clone().push_str("Bar") 求值为 (), the unit type. The compiler error is correct, because the push_str 函数的类型是 fn push_str(&mut self, string: &str),注意它没有 return 类型,而是改变了它的 Self 参数。

相反,您需要先推入字符串,然后 return 推入字符串,例如

impl AppendBar for String {
    fn append_bar(mut self) -> Self{
        self.push_str("Bar");
        self
    }
}

我还删除了 .clone(),因为没有必要。 append_bar 已经接受 self 并因此获得字符串值的所有权,因此您可以将其推入并 return 它而无需克隆它。

@loganfsmyth 的回答解释了您收到此错误消息的原因。根据您对append_bar:

的期望,可以通过三种方式解决

取得所有权

如果您希望 append_bar 到 return 修改后的字符串并且不希望调用者之后能够使用输入字符串:

impl AppendBar for String {
    fn append_bar (mut self) -> Self {
        self.push_str ("Bar");
        self
    }
}
let s1 = String::from ("Foo");
let s2 = s1.append_bar();
// println!("s1: {}", s1);    // Error: s1 is no longer usable at this point
println!("s2: {}", s2);    // Prints "FooBar"

Playground

(这与@loganfsmyth 的回答相同)。

借用和克隆

如果您希望 append_bar 到 return 修改后的字符串并希望调用者之后能够继续使用原始输入字符串:

impl AppendBar for String {
    fn append_bar (&self) -> Self {
        let mut s = self.clone();
        s.push_str ("Bar");
        s
    }
}
let s1 = String::from ("Foo");
let s2 = s1.append_bar();
println!("s1: {}", s1);    // Prints "Foo"
println!("s2: {}", s2);    // Prints "FooBar"

Playground

原地变异

如果您希望append_bar将输入替换为修改后的字符串:

impl AppendBar for String {
    fn append_bar (&mut self) {
        self.push_str ("Bar");
    }
}
let mut s1 = String::from ("Foo");
s1.append_bar();
println!("s1: {}", s1);    // Prints "FooBar"

Playground