Черта 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 }
}