generics - 重写一个函数以接受AsRef <Path>而不是&Path

标签 generics rust traits lifetime borrow-checker

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

fn find_database1<'a>(path: &'a Path) -> Option<&'a Path> {
    path.parent()
}
编写完上述函数后,我想将其转换为一种形式,不仅接受Path,而且接受String&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

最佳答案

Path::parent 具有以下签名:

fn parent(&self) -> Option<&Path>;
因此,返回的结果将保留对调用方拥有的某些数据的引用。您不能在parent()上调用String,然后再删除String,因为这会使parent()返回的引用无效。如果您放宽了对String的接受并改为接受&String的要求,则可以使函数正常工作。例子:
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
另一方面,如果您确实想接受String,则可以将Option<&Path>转换为函数体内的Option<PathBuf>。这是有效的,因为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

关于generics - 重写一个函数以接受AsRef <Path>而不是&Path,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62603012/

相关文章:

c# - 如何使用泛型在基类的方法中指定输出参数的类型?

java - Java 中的泛型无效

rust - 如何用u64::max_value()填充Vec

python - 使用适配器时如何在 TraitsUI TreeView 中启用右键单击菜单?

rust - 使用具有特征的泛型

Java 泛型 : method signature for (deep copy of) generic Maps

class - Typescript 访问泛型类型的静态属性

multithreading - 对字段成员的Arc引用

rust - 参数数量/类型不匹配

scala - 重写 Scala 中的密封特征