• [翻译] Rust 的 Api 设计 (AsRef, Into, Cow)


    原文地址

    函数参数重载

    Rust 不支持函数参数重载, 但是我们可以使用内置的 trait 来实现类似的功能. 它们就是 AsRefInto.

    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);
    

    该函数定义限制了 p1p2 必须为相同类型, 如下更改可以使其更灵活:

    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 不能产生错误或异常. 如果需要可以参考 TryFromTryInto.

    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!"),
        }
    }
    

    参考文献

  • 相关阅读:
    DELPHI版传奇引擎学习菜鸟篇(applem2)04
    DELPHI版传奇引擎学习菜鸟篇(applem2)03
    学习win32API消息处理
    DELPHI版传奇引擎学习菜鸟篇(applem2)02
    DELPHI版传奇引擎学习菜鸟篇(applem2)01
    读写INI的通用函数
    黑马程序员选择语句 SwitchCase 和break 、return 关键字。
    黑马程序员Readonly和Const的区别
    黑马程序员计算每个字符在字符串中出现的次数
    黑马程序员ADO.NET中的五个主要对象
  • 原文地址:https://www.cnblogs.com/wbin91/p/14217221.html
Copyright © 2020-2023  润新知