从字符串的随机 Vec 中读取文件

read file from random Vec of string

我想从 Vec<String> 中随机读取一个文件,然后将其用作 multipart 以请求异步

但我不断收到:

这是函数

fn getFiles(dirname: &str) -> Vec<String> {
    let mut items: Vec<String> = Vec::new();
    let dir = std::fs::read_dir(dirname);
    for item in dir.expect("fail") {
        if let Ok(item) = item {
            items.push(item.path().into_os_string().into_string().unwrap());
        }
    }
    items
}
    // call the function
    let dir_items = getFiles("src/assets");
    let file = dir_items.into_iter().choose(&mut rand::thread_rng()).unwrap();
    let path = std::path::Path::new(&file);
    let sub_file = std::fs::read(path)?;
    // after this, file lifetime is already end ?
    let sub_file_part = reqwest::multipart::Part::bytes(sub_file)
        .file_name(path.file_name().unwrap().to_string_lossy())
        .mime_str("application/octet-stream")?;

playground

让我们一块一块看:

let path = std::path::Path::new(&file);

如果您查看 the documentation for Path::new,您会看到以下签名:

pub fn new<S: AsRef<OsStr> + ?Sized>(s: &S) -> &Path

在应用第二个 lifetime elision rules 之后,(引用:“如果只有一个输入生命周期参数,则该生命周期被分配给所有输出生命周期参数:fn foo<'a>(x: &'a i32) -> &'a i32”),这个签名看起来确实像这样:

pub fn new<'a, S: AsRef<OsStr> + ?Sized>(s: &'a S) -> &'a Path

因此,您的 path 变量不能比您的 file 变量生存得更久。

接下来是相关的:

.file_name(path.file_name().unwrap().to_string_lossy())

再次应用生命周期省略规则后,您可以看到调用 unwrap on path.file_name() gives you a &std::ffi::OsStr 的生命周期不会超过您的 path 变量,而后者(如前所述)的生命周期不会超过您的file 变量。因此,可传递地,path.filename().unwrap() 的寿命不能超过 file 变量。

现在,让我们看看OsStr::to_string_lossy()的签名:

pub fn to_string_lossy(&self) -> Cow<'_, str>

所以,这个方法returns一个Cow<'_, str>Cow 是 shorthand 表示“复制或拥有”——这意味着 Cow 可以包含对数据的引用(在这种情况下,&str,如果 OsStr 仅包含有效的 UTF-8 字节)或者它可以拥有数据本身(在这种情况下,通过包含 String,如果 OsStr 包含需要转换或过滤的数据在 UTF-8 转换期间输出)。

'_is a placeholder and means that the compiler shall (again) infer the lifetime with the lifetime elision rules。扩展签名如下所示:

pub fn to_string_lossy<'a>(&'a self) -> Cow<'a, str>

因此,在您的示例中,'a 不能大于 unwrap() 返回的 &OsStr 的生命周期,这间接意味着 'a 可以' 大于 file.

的生命周期

但是,reqwest::multipart::Part::file_name() 需要一些实现 Into<Cow<'static, str>> 作为参数的东西。我们的 Cow<'a, str> 绝对不能实现它,因为我们的 file 变量直到程序结束才有效。如果它 存活到程序结束,我们的 Cow 将实现它,因为 file 可以借给 'static,并且所有的都将是好吧——这就是错误消息的原因。

您可以通过在 Cow 上调用 into_owned() 来解决这个问题。这会将 Cow 转换为字符串, 实现 Into<Cow<'static, str>>:

use rand::prelude::*;

fn getFiles(dirname: &str) -> Vec<String> {
    let mut items: Vec<String> = Vec::new();
    let dir = std::fs::read_dir(dirname);
    for item in dir.expect("fail") {
        if let Ok(item) = item {
            items.push(item.path().into_os_string().into_string().unwrap());
        }
    }
    items
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // call the function
    let dir_items = getFiles("src/assets");
    let file = dir_items.into_iter().choose(&mut rand::thread_rng()).unwrap();
    let path = std::path::Path::new(&file);
    let sub_file = std::fs::read(path)?;
    // after this, file lifetime is already end ?
    let sub_file_part = reqwest::multipart::Part::bytes(sub_file)
        .file_name(path.file_name().unwrap().to_string_lossy().into_owned())
        .mime_str("application/octet-stream")?;
    Ok(())
}

Playground