Черта From<& String> не реализована для типа 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 черта превратит &str в строку, которую я мог бы использовать. Что я делаю неправильно?

1 ответ

Решение

Вы объединяете два разных процесса.

Во-первых, есть принуждение; особенно, Deref принуждение. Это происходит, когда компилятор видит, что у вас есть &U, но вы хотите &T, При условии, что есть impl Deref<Target=T> for U, это сделает принуждение для вас. Вот почему &String приведет к &str,

Однако, это не вступает в игру, когда компилятор заменяет параметры универсального типа. Когда ты сказал BigNum::new(&line)компилятор видит, что вы пытаетесь передать &String где он ожидает S; Таким образом, S должно быть &Stringтаким образом S должен реализовать Into<String> и... о нет! Это не так! БУМ! Принуждение никогда не запускается, потому что компилятору никогда не нужно ничего принуждать; невыполненные ограничения типа - это другая проблема.

В этом конкретном случае, что вы должны сделать, зависит от ваших обстоятельств:

  • Вы можете просто передать String; использование line или же line.clone(), Это самый эффективный способ, который вы всегда можете передать в собственность. String вам больше не нужно и избегайте лишних выделений.

  • Вы можете вместо этого взять &S с S: ?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);
}

Как упоминалось Д.К., это невозможно с Rust Into черта, из-за отсутствия реализации Into<String> for &String, Я не могу найти причины этого, но вы можете создать свою собственную черту, чтобы решить эту проблему:

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 }
}
Другие вопросы по тегам