我试图理解你如何返回非原语(即没有实现Copy
的类型)。如果返回类似i32
的内容,则该函数会在内存中创建一个带有返回值副本的新值,因此可以在函数范围之外使用它。但是,如果您返回的类型未实现Copy
,则不会执行此操作,并且您将获得所有权错误。
我尝试使用Box
在堆上创建值,以便调用者可以获取返回值的所有权,但这似乎也不起作用。
也许我通过使用我在C#或其他语言中使用的相同编码样式以错误的方式接近它,其中函数返回值,而不是将对象引用作为参数传递并对其进行变更,以便您可以很容易表明Rust的所有权。
以下代码示例无法编译。我相信这个问题只在迭代器闭包内,但我已经包含了整个函数,以防万一我没有看到。
pub fn get_files(path: &Path) -> Vec<&Path> {
let contents = fs::walk_dir(path);
match contents {
Ok(c) => c.filter_map(|i| { match i {
Ok(d) => {
let val = d.path();
let p = val.as_path();
Some(p)
},
Err(_) => None } })
.collect(),
Err(e) => panic!("An error occurred getting files from {:?}: {}", pa
th, e)
}
}
编译器出现以下错误(我删除了所有行号和无关文本):
error: `val` does not live long enough
let p = val.as_path();
^~~
in expansion of closure expansion
expansion site
reference must be valid for the anonymous lifetime #1 defined on the block...
...but borrowed value is only valid for the block suffix following statement
let val = d.path();
let p = val.as_path();
Some(p)
},
答案 0 :(得分:8)
您可以通过...返回一个值将其返回。但是,您的签名表明您尝试返回对值的引用。当对象在块的末尾被删除时,你不能这样做,因为引用会变得无效。
在你的情况下,我可能会写类似
的内容#![feature(fs_walk)]
use std::fs;
use std::path::{Path, PathBuf};
fn get_files(path: &Path) -> Vec<PathBuf> {
let contents = fs::walk_dir(path).unwrap();
contents.filter_map(|i| {
i.ok().map(|p| p.path())
}).collect()
}
fn main() {
for f in get_files(Path::new("/etc")) {
println!("{:?}", f);
}
}
主要的是该函数返回一个Vec<PathBuf>
- 拥有路径的类型的集合,而不仅仅是对别人内存的引用。
在您的代码中,您执行let p = val.as_path()
。在此,val
是PathBuf
。然后拨打as_path
,其定义为:fn as_path(&self) -> &Path
。这意味着,如果引用PathBuf
,您可以获得Path
的引用,只要PathBuf
,就会生效。但是,您试图保持该引用的存在时间长于vec
,因为它将在迭代结束时被删除。
答案 1 :(得分:3)
如何退回不可复制的类型?
按价值计算。
fn make() -> String { "Hello, World!".into() }
以下之间存在脱节:
从语义上讲,按值返回移动对象,而不是复制它。在Rust中,任何对象都是可移动的,并且可选地,也可以是Clonable(实现Clone
)和Copyable(实现Clone
和Copy
)。
复制或移动的实现在引擎盖下使用memcpy
是一个细节,不会影响语义,只会影响性能。此外,这是一个实现细节意味着它可以在不影响语义的情况下进行优化,优化器将非常努力地去做。
至于您的特定代码,您有一个终身问题。如果所述引用可能比该值更长,则不能返回对值的引用(那么,它将引用什么?)。
简单的解决方法是返回值本身:Vec<PathBuf>
。如上所述,它将移动路径,而不是复制它们。