From<&String> 特性没有为 String 类型实现

the From<&String> trait is not implemented for the type String

我要离开 this 文章,试图编写一个既接受字符串又接受 &str 的函数,但我 运行 遇到了问题。我有以下功能:

pub fn new<S>(t_num: S) -> BigNum where S: Into<String> {
    let t_value = t_num.into();
    let t_digits = t_value.len();
    BigNum { value: t_value, digits: t_digits }
}

BigNum 是一个简单的结构,但是问题是当我试图用 &collections::string::String 调用它时出现错误:

let line = "123456".to_string()
let big = bignum::BigNum::new(&line)

main.rs:23:15: 23:34 error: the trait `core::convert::From<&collections::string::String>` is not implemented for the type `collections::string::String` [E0277]
main.rs:23     let big = bignum::BigNum::new(&line);

我的印象是 &String 会隐式分解为 &str 不是吗?在这种情况下,Into 特性会将 &str 转换为我可以使用的字符串。我做错了什么?

您将两个不同的过程混为一谈。

一是胁迫;特别是 Deref coercion。当编译器发现您有 &U,但您 想要 &T 时,就会发生这种情况。只要有 impl Deref<Target=T> for U,它就会为你强制转换。这就是为什么 &String 会强制转换为 &str.

但是,当编译器替换泛型类型参数时,不会发挥作用。当你说 BigNum::new(&line) 时,编译器看到的是你试图在它期望 S 的地方传递 &String;因此,S 必须是 &String,因此 S 必须实现 Into<String> 并且...哦不!它没有! BOOM! 永远不会触发强制转换,因为编译器永远不需要 强制转换任何东西;未实现的类型约束是一个不同的问题。

在这个特殊的案例中,你应该做什么取决于你的情况:

  • 你可以传一个String;使用 lineline.clone()。这是最有效的,因为您始终可以传入一个您不再需要的拥有的 String 并避免额外分配。

  • 您可以使用 &SS: ?Sized + AsRef<str>,这不允许您传递拥有的字符串,但是如果您总是要分配,这可能更符合人体工学。

以下是两者的实际应用示例:

use std::convert::AsRef;

fn main() {
    take_a_string(String::from("abc"));
    // take_a_string(&String::from("abc")); // Boom!
    take_a_string("def");

    // take_a_string_ref(String::from("abc")); // Boom!
    take_a_string_ref(&String::from("abc"));
    take_a_string_ref("def");
}

fn take_a_string<S>(s: S)
where S: Into<String> {
    let s: String = s.into();
    println!("{:?}", s);
}

fn take_a_string_ref<S: ?Sized>(s: &S)
where S: AsRef<str> {
    let s: String = s.as_ref().into();
    println!("{:?}", s);
}

正如 DK. 所提到的,这对于 Rust Into 特性是不可能的,因为缺少 Into<String> for &String 的实现。我找不到这背后的原因,但你可以创建自己的 Trait 来解决这个问题:

pub trait IntoString {
    fn into(self) -> String;
}

impl IntoString for &String {
    fn into(self) -> String {
        self.to_string()
    }
}
impl IntoString for &str {
    fn into(self) -> String {
        self.to_string()
    }
}

impl IntoString for String {
    fn into(self) -> String {
        self
    }
}

pub fn new<S>(t_num: S) -> BigNum where S: IntoString {
    let t_value = t_num.into();
    let t_digits = t_value.len();
    BigNum { value: t_value, digits: t_digits }
}