类型String没有实现From<&String> trait。

12

我正参考 这篇 文章尝试撰写一个接收 String 和 &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 trait 会将&str转换为我可以使用的字符串。我做错了什么?
2个回答

12

您混淆了两个不同的过程。

首先,有强制转换; 特别是 Deref coercion。 当编译器看到您有一个 &U,但您 需要 一个 &T 时,就会发生这种情况。 只要有一个 impl Deref<Target=T> for U,它就会为您进行强制转换。 这就是为什么 &String 会转换为 &str 的原因。

然而,在编译器替换通用类型参数时,此规则不适用。 当您使用 BigNum::new(&line) 时,编译器看到的是您正在尝试传递一个 &String 而它期望一个 S;因此,S 必须是 &String,因此 S 必须实现 Into<String>,但是......糟糕了!强制转换永远不会触发,因为编译器从未需要强制转换任何内容;无法满足的类型约束是另一个问题。崩溃了!

在这个 特殊 情况下,您应该根据您的情况采取不同的方法:

  • 你可以直接传递一个字符串;使用 line 或者 line.clone()。这是最有效的方法,因为你总是可以传递一个你不再需要的拥有权的字符串,避免额外的分配。

  • 相反,你可以采用 &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);
}

0

正如DK所提到的,由于缺少Into<String> for &String的实现,这在Rust的Into特性中是不可能的。我找不到背后的原因,但你可以创建自己的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 }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接