函数参数重载
Rust 不支持函数参数重载, 但是我们可以使用内置的 trait 来实现类似的功能. 它们就是 AsRef 和 Into.
AsRef (和 AsMut)
AsRef 允许调用函数时提供的参数类型不一样 - 只要函数接受的是一个引用类型, 而传入的类型能够转成改引用类型. AsMut 实现相同功能只不过是对 mut 引用.
一个例子:
fn func1(p1: PathBuf);
fn func2(p1: &Path);
fn func3<S: AsRef<Path>>(p1: S) {
let p1 = p1.as_ref();
...
}
func1
可以传递类型PathBuf
的参数func2
可以传递类型为Path
或者&PathBuf
func3
可以传递类型为Path
或者&PathBuf
, 或者任意可以 borrow 一个&Path
的类型, 比如说,String
func3
函数在使用时更灵活.
考虑如下 func4
:
fn func4<S: AsRef<Path>>(p1: S, p2: S);
该函数定义限制了 p1
和 p2
必须为相同类型, 如下更改可以使其更灵活:
fn func4<N: AsRef<Path>, M: AsRef<Path>>(p1: N, p2: M);
AsRef
一个常见的用例是将值转换成(静态)字符串, 比如 enum 值. 我们可以直接使用 AsRef
来实现:
impl AsRef<str> for XmlDoc {
fn as_ref(&self) -> &str {
XmlDoc::Unknown => "Unknown",
XmlDoc::None => "None",
XmlDoc::Debug => "Debug",
XmlDoc::Release => "Release",
XmlDoc::Both => "Both",
}
}
AsRef 反模式
AsRef 适用于只需要传参的引用时使用. 如果你发现参数所有权时, 比如
let x = param.as_ref().to_owned();
那么你应该使用 Into
triat.
Into
Into 类似于 AsRef, 可以让我们在调用函数时传参更灵活. 不同的是使用 Into 时需要参数的所有权. 我们经常在构造函数中使用:
fn new<S: Into<String>>(model: S) -> Self;
为自定义类型实现 Into
不要为自定义的类型实现 Into
. 转而使用 From
实现来, 这样我们就可以使用标准库中 Into
的默认实现了.
如果你的构造函数只有一个类型为 T
的参数, 那么我强烈建议你不要构造函数使用 From<T>
代替.
impl<P> From<P> for SolutionDirectory
where P: Into<PathBuf>
{
fn from(sln_directory: P) -> Self {
SolutionDirectory {
directory: sln_directory.into(),
solutions: vec![]
}
}
}
let s: SolutionDirectory = "somepath".into();
Tips
- 标准库
std::fs
中的函数, 如果接收参数类型为AsRef<Path>
, 那么可以传参类型PathBuf
,Path
,String
,&str
,OsString
,OsStr
以及其他. Into<String>
可以接收常用字符串类型以及Box<str>
,Rc<String>
,Cow<str>
等.- 上述 trait 不能产生错误或异常. 如果需要可以参考
TryFrom
和TryInto
.
Cow 作为返回值
Cow
类型可以延迟内存分配. 该类型经常出现在返回值位置(该类型实现了 Deref
, 因此输入参数不需要使用该类型, 直接用 &T
就可以了).
use std::borrow::Cow;
/// 返回 Cow::Borrowed 类型, 静态生命周期
fn func5() -> Cow<'static, str> {
"".into()
}
/// 返回 Cow::Owned, 静态生命周期
fn func6() -> Cow<'static, str> {
let s = "s".to_owned();
s.into()
}
/// 返回 Cow::Borrowed, 并且生命周期和 'data' 相同.
fn func7(data: &str) -> Cow<str> {
Cow::Borrowed(&data[0..1])
}
// Ditto.
fn func8(data: &str) -> Cow<str> {
data[0..1].into()
}
fn main() {
match func8("hello") {
Cow::Borrowed(_) => println!("It's borrowed"),
Cow::Owned(_) => println!("Owned!"),
}
}