我要离开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
转换为我可以使用的字符串。我做错了什么?
答案 0 :(得分:6)
你正在混淆两个不同的过程。
首先,有强制;尤其是Deref
coercion。当编译器发现您有&U
,但想要 &T
时会发生这种情况。如果有impl Deref<Target=T> for U
,它会为你做强制。这就是&String
强制转换为&str
。
然而,当编译器替换泛型类型参数时,此不会发挥作用。当你说BigNum::new(&line)
时,编译器看到的是你试图传递&String
所需的S
;因此,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);
}
答案 1 :(得分:0)
正如DK。所提到的,由于缺少Into
的实现,因此使用Rust 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 }
}