【问题标题】:Rewriting a function to accept AsRef<Path> instead of &Path重写一个函数以接受 AsRef<Path> 而不是 &Path
【发布时间】:2020-06-26 21:22:46
【问题描述】:

如何编写以下函数以不仅接受Path,还接受String&amp;str

fn find_database1<'a>(path: &'a Path) -> Option<&'a Path> {
    path.parent()
}

写完上面提到的函数后,我想把它转换成一个形式,不仅接受Path,还接受String&amp;str。我最终得到了以下两个版本,每个版本都不起作用。函数find_database3 试图更好地了解原因,但不幸的是我不明白为什么它不起作用。

fn find_database2<'a, P>(path: P) -> Option<&'a Path>
where
    P: 'a + AsRef<Path>,
{
    path.as_ref().parent()
}

fn find_database3<'a, P>(path: P) -> Option<&'a Path>
where
    P: 'a + AsRef<Path>,
{
    let _path: &'a Path = path.as_ref();
    _path.parent()
}

这些是我得到的错误:

error[E0515]: cannot return value referencing function parameter `path`
  --> src/main.rs:11:5
   |
11 |     path.as_ref().parent()
   |     ----^^^^^^^^^^^^^^^^^^
   |     |
   |     returns a value referencing data owned by the current function
   |     `path` is borrowed here

error[E0597]: `path` does not live long enough
  --> src/main.rs:18:27
   |
14 | fn find_database3<'a, P>(path: P) -> Option<&'a Path>
   |                   -- lifetime `'a` defined here
...
18 |     let _path: &'a Path = path.as_ref();
   |                --------   ^^^^ borrowed value does not live long enough
   |                |
   |                type annotation requires that `path` is borrowed for `'a`
19 |     _path.parent()
20 | }
   | - `path` dropped here while still borrowed
use std::path::Path;

fn main() {
    let path_str: &str = "root/path";
    let path_string: String = path_str.to_string();
    let path_path: &Path = &Path::new(path_str);

    let root = find_database1(path_path);
    println!("{:?}", root);

    find_database2(path_str);
    find_database2(path_string);
    let root = find_database2(path_path);
    println!("{:?}", root);
}

Link to Playground

【问题讨论】:

    标签: generics rust traits lifetime borrow-checker


    【解决方案1】:

    Path::parent 有这个签名:

    fn parent(&self) -> Option<&Path>;
    

    因此返回的结果包含对调用者拥有的某些数据的引用。您不能在String 上调用parent(),然后删除String,因为这会使parent() 返回的引用无效。如果您放宽使用Strings 的要求并改为接受&amp;Strings,则可以使您的功能正常工作。示例:

    use std::path::Path;
    
    // takes &str, &String, or &Path
    fn find_database2<'a, P>(path: &'a P) -> Option<&'a Path>
        where P: 'a + ?Sized + AsRef<Path>,
    {
        path.as_ref().parent()
    }
    
    fn main() {
        let path_str: &str = "root/path";
        let path_string: String = path_str.to_string();
        let path_path: &Path = &Path::new(path_str);
    
        find_database2(path_str); // &str
        find_database2(&path_string); // &String
        let root = find_database2(path_path); // &Path
        println!("{:?}", root);
    }
    

    playground

    另一方面,如果您真的想接受Strings,您可以在函数体内将Option&lt;&amp;Path&gt; 转换为Option&lt;PathBuf&gt;。这是有效的,因为PathBufPath 的拥有版本:

    use std::path::{Path, PathBuf};
    
    // takes &str, &String, String, &Path, or PathBuf
    fn find_database2<'a, P>(path: P) -> Option<PathBuf>
        where P: 'a + AsRef<Path>,
    {
        path.as_ref().parent().map(|path| {
            let mut path_buf = PathBuf::new();
            path_buf.push(path);
            path_buf
        })
    }
    
    fn main() {
        let path_str: &str = "root/path";
        let path_string: String = path_str.to_string();
        let path_path: &Path = &Path::new(path_str);
    
        find_database2(path_str); // &str
        find_database2(&path_string); // &String
        find_database2(path_string); // String
        let root = find_database2(path_path); // &Path
        println!("{:?}", root);
    }
    

    playground

    【讨论】:

    • 感谢您的解释!在再考虑之后,您的第一个版本(接受 &String 而不是 String)是我真正想要的版本。您的示例还帮助我更好地理解 ?Sized 标记。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-04
    • 2012-04-23
    • 1970-01-01
    • 1970-01-01
    • 2021-12-23
    • 2022-07-16
    相关资源
    最近更新 更多