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
;使用 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);
}
正如 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 }
}
我要离开 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
;使用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);
}
正如 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 }
}