我对 rust 完全陌生。我创建了一个程序来存储有关人员的信息,以便与该语言取得联系:
person.rs
pub struct Person {
firstname: String,
lastname: String,
pub age: u8
}
impl Person {
pub fn new(firstname: String, lastname: String, age: u8) -> Person {
return Person { firstname: firstname, lastname: lastname, age: age };
}
pub fn from_str(firstname: &str, lastname: &str, age: u8) -> Person {
return Person::new(firstname.to_string(), lastname.to_string(), age);
}
pub fn name(&self) -> String {
return self.firstname.to_owned() + " " + &self.lastname;
}
}
main.rs
mod person;
use person::Person;
fn main() {
let me = Person::from_str("John", "Doe", 42);
println!("{} is {} years old.", me.name(), me.age);
}
我知道,rust 中没有函数重载。 但是,我读过一个叫做"特质"的概念,我还没有完全理解。 它们的行为是否类似于C++中的模板(我也不是C++专业人士)? 有没有另一种方法,例如使用特征,以便我可以编写Person::new()
来接受String
或&str
?
我试过这个无济于事:
person.rs
pub struct Person {
firstname: String,
lastname: String,
pub age: u8
}
impl Person {
/*
pub fn new(firstname: String, lastname: String, age: u8) -> Person {
return Person { firstname: firstname, lastname: lastname, age: age };
}
pub fn from_str(firstname: &str, lastname: &str, age: u8) -> Person {
return Person::new(firstname.to_string(), lastname.to_string(), age);
}
*/
pub fn new<T>(firstname: &T, lastname: &T, age: u8) -> Person {
return Person::new(firstname.to_string(), lastname.to_string(), age);
}
pub fn name(&self) -> String {
return self.firstname.to_owned() + " " + &self.lastname;
}
}
main.rs
mod person;
use person::Person;
fn main() {
let me = Person::new("John", "Doe", 42);
println!("{} is {} years old.", me.name(), me.age);
}
结果是:
$ rustc main.rs -o main
error[E0599]: the method `to_string` exists for reference `&T`, but its trait bounds were not satisfied
--> person.rs:20:38
|
20 | return Person::new(firstname.to_string(), lastname.to_string(), age);
| ^^^^^^^^^ method cannot be called on `&T` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`T: std::fmt::Display`
which is required by `T: ToString`
`&T: std::fmt::Display`
which is required by `&T: ToString`
error[E0599]: the method `to_string` exists for reference `&T`, but its trait bounds were not satisfied
--> person.rs:20:60
|
20 | return Person::new(firstname.to_string(), lastname.to_string(), age);
| ^^^^^^^^^ method cannot be called on `&T` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`T: std::fmt::Display`
which is required by `T: ToString`
`&T: std::fmt::Display`
which is required by `&T: ToString`
error[E0277]: the size for values of type `str` cannot be known at compilation time
--> main.rs:6:14
|
6 | let me = Person::new("John", "Doe", 42);
| ^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `str`
note: required by a bound in `Person::new`
--> person.rs:19:16
|
19 | pub fn new<T>(firstname: &T, lastname: &T, age: u8) -> Person {
| ^ required by this bound in `Person::new`
help: consider relaxing the implicit `Sized` restriction
--> person.rs:19:17
|
19 | pub fn new<T: ?Sized>(firstname: &T, lastname: &T, age: u8) -> Person {
| ++++++++
error: aborting due to 3 previous errors
Some errors have detailed explanations: E0277, E0599.
For more information about an error, try `rustc --explain E0277`.
Rust 有泛型,而不是模板。泛型可以由特征限制,这些特征为标准接口提供了一个系统。
在您的情况下,您需要的是一种特质,可以让您从String
或&str
中获得String
。你有几个选择,但我会选择Into<String>
.
您可以这样做:
pub fn new(firstname: impl Into<String>, lastname: impl Into<String>, age: u8) -> Person {
Person {
firstname: firstname.into(),
lastname: lastname.into(),
age
}
}
从本质上讲,这说的是:
- 新是一个函数
- 这需要三个参数
- 前两个参数可以是实现
Into<String>
的任何类型 - 最后一个参数必须是
u8
Into<T>
是 Rust 中的一个标准特征,用于转换。它定义了一个关联的fn into(self) -> T
,该按值获取给定对象并返回目标T
。
Into
实际上只是From<T>
的补充帮助程序特征,它具有关联的fn from(other: T) -> Self
,该采用其他类型并返回所需的类型。
From
有一个反身实现,这本质上意味着对于任何类型,您都可以在x
属于T
类型的地方执行T::from(x)
。这也适用于Into
,因此任何类型都可以into
自身转换,这就是为什么String
在编译器期望Into<String>
时工作的原因。
你可以接受impl Into<String>
:
pub fn new(firstname: impl Into<String>, lastname: impl Into<String>, age: u8) -> Person {
Person {
firstname: firstname.into(),
lastname: lastname.into(),
age: age,
}
}
参数位置impl Trait
是声明参数是泛型的简短方法。您也可以使用显式命名的泛型类型写出函数签名:
pub fn new<F, L>(firstname: F, lastname: L, age: u8) -> Person
where
F: Into<String>,
L: Into<String>,
要过多详细解释Into
的使用:
Into
是From
的反面,这要归功于这种一揽子实现:impl<T, U> const Into<U> for T where U: ~const From<T>, { fn into(self) -> U { U::from(self) } }
只要存在
impl From<T> for U
,匹配的impl Into<U> for T
也会存在。标准库为
String
提供了多种这样的实现:impl From<char> for String impl From<Box<str>> for String impl From<&str> for String impl From<&String> for String impl From<&mut str> for String impl From<Cow<'_, str>> for String
多亏了
impl From<&str> for String
,您可以从&str
构建String
。此外,所有
T
都有一个全面的实现:impl<T> From<T> for T { fn from(t: T) -> T { t } }
任何类型都可以简单地转换为自身。这意味着
impl From<String> for String
也可以作为零成本转换使用。