Как понять эту ошибку заимствованной стоимости? Создание Vec ‹& PathBuf›

Как мне понять, какую часть проверки заимствований я нарушаю?

Поскольку стандартная библиотека Rust walk_dir указана как "нестабильная" (как of 2015-09-27), я подумал, что попытаюсь создать свою собственную функцию, чтобы захватывать все файлы в каталоге и его дочерние каталоги самостоятельно.

Вот что у меня есть для того, чтобы просто перечислить файлы в каталоге, не глядя на часть дочернего каталога:

use std::fs::File;
use std::path::{Path,PathBuf};

fn get_files(this_path: &Path) -> Vec<&PathBuf>{
    let contents = fs::read_dir(this_path).unwrap();
    let mut output: Vec<&PathBuf> = Vec::new();

    for path in contents {
        let p = path.unwrap().path();
        if fs::metadata(&p).unwrap().is_dir() {
            // dunno, recursively append files to output 
        } else if fs::metadata(&p).unwrap().is_file() {
            output.push(&p)
        }
    }

    return output;
}

fn main() {
    for f in get_files(Path::new(".")) {
        println!("{}", f.display())
    }
}

Когда я пытаюсь запустить этот код, я получаю такую ​​ошибку:

src/main.rs:58:26: 58:27 error: `p` does not live long enough
src/main.rs:58             output.push(&p)
                                        ^
note: in expansion of for loop expansion
src/main.rs:53:5: 60:6 note: expansion site
src/main.rs:49:48: 63:2 note: reference must be valid for the anonymous lifetime #1 defined on the block at 49:47...
src/main.rs:49 fn get_files(this_path: &Path) -> Vec<&PathBuf>{
src/main.rs:50     let contents = fs::read_dir(this_path).unwrap();
src/main.rs:51     let mut output: Vec<&PathBuf> = Vec::new();
src/main.rs:52
src/main.rs:53     for path in contents {
src/main.rs:54         let p = path.unwrap().path();
               ...
src/main.rs:54:38: 60:6 note: ...but borrowed value is only valid for the block suffix following statement 0 at 54:37
src/main.rs:54         let p = path.unwrap().path();
src/main.rs:55         if fs::metadata(&p).unwrap().is_dir() {
src/main.rs:56             // dunno, recursively append files to output
src/main.rs:57         } else if fs::metadata(&p).unwrap().is_file() {
src/main.rs:58             output.push(&p)
src/main.rs:59         }
               ...
error: aborting due to previous error

Поправьте меня, если я ошибаюсь, но у меня очень слабое представление о том, что одна из замечательных особенностей Rust заключается в том, что вы должны явно объявлять, когда объекты должны жить после области видимости функции. Я думаю, что моя проблема в том, что PathBuf, созданный в let p = path.unwrap().path(), отбрасывается в конце итерации цикла for, поэтому output Vec содержит ссылку на то, что исчезло.

В таком случае:

Как мне развить лучшую интуицию, когда я делаю что-то вроде этого тупого?

Есть ли лучший идиоматический способ создать вектор элементов из функций, возвращающих ресурсы с ограниченным временем жизни?


person Conrad.Dean    schedule 27.09.2015    source источник


Ответы (1)


Интуиция здесь такая:

Я не могу вернуть ссылку на что-то, созданное внутри функции, потому что оно будет освобождено в конце этой функции, что сделает ссылку недействительной.

Вместо этого вам нужно убрать эту штуку. Следовательно, Vec<PathBuf>, собственный вариант Vec<&PathBuf> (поскольку PathBuf является собственным вариантом &PathBuf) должен быть вместо этого вашим типом возврата.

person Steve Klabnik    schedule 27.09.2015
comment
оххх, собственный вариант & PathBuf является PathBuf? - person Conrad.Dean; 27.09.2015
comment
Ой! Я думаю, что переполнение мобильного стека отредактировало мои «ы», позвольте мне отредактировать - person Steve Klabnik; 27.09.2015
comment
Круто, так что, возвращая принадлежащие варианты, право собственности переходит к вызывающему моего get_files, что в любом случае было намерением. так лучше - person Conrad.Dean; 27.09.2015
comment
Точно! Вот почему Rust крут, компилятор только что спас вас от ошибки во время выполнения с висящим указателем на другом языке. - person Steve Klabnik; 27.09.2015