你如何归还不可复制的类型?

时间:2015-08-16 21:54:30

标签: function rust

我试图理解你如何返回非原语(即没有实现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)
                 },

2 个答案:

答案 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()。在此,valPathBuf。然后拨打as_path,其定义为:fn as_path(&self) -> &Path。这意味着,如果引用PathBuf,您可以获得Path 的引用,只要PathBuf ,就会生效。但是,您试图保持该引用的存在时间长于vec,因为它将在迭代结束时被删除。

答案 1 :(得分:3)

如何退回不可复制的类型?

按价值计算。

fn make() -> String { "Hello, World!".into() }

以下之间存在脱节:

  • 语言语义
  • 实施细节

从语义上讲,按值返回移动对象,而不是复制它。在Rust中,任何对象都是可移动的,并且可选地,也可以是Clonable(实现Clone)和Copyable(实现CloneCopy)。

复制或移动的实现在引擎盖下使用memcpy是一个细节,不会影响语义,只会影响性能。此外,这是一个实现细节意味着它可以在不影响语义的情况下进行优化,优化器将非常努力地去做。

至于您的特定代码,您有一个终身问题。如果所述引用可能比该值更长,则不能返回对值的引用(那么,它将引用什么?)。

简单的解决方法是返回值本身:Vec<PathBuf>。如上所述,它将移动路径,而不是复制它们。