Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you return non-copyable types?

Tags:

function

rust

I am trying to understand how you return non-primitives (i.e. types that do not implement Copy). If you return something like a i32, then the function creates a new value in memory with a copy of the return value, so it can be used outside the scope of the function. But if you return a type that doesn't implement Copy, it does not do this, and you get ownership errors.

I have tried using Box to create values on the heap so that the caller can take ownership of the return value, but this doesn't seem to work either.

Perhaps I am approaching this in the wrong manner by using the same coding style that I use in C# or other languages, where functions return values, rather than passing in an object reference as a parameter and mutating it, so that you can easily indicate ownership in Rust.

The following code examples fails compilation. I believe the issue is only within the iterator closure, but I have included the entire function just in case I am not seeing something.

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) 
      } 
 }

The compiler gives the following error (I have removed all the line numbers and extraneous text):

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)
                 },
like image 739
CalMlynarczyk Avatar asked Aug 16 '15 21:08

CalMlynarczyk


People also ask

How do you make a class not copyable?

In some cases, an instance of a C++ class should not be copied at all. There are three ways to prevent such an object copy: keeping the copy constructor and assignment operator private, using a special non-copyable mixin, or deleting those special member functions.

What is boost NonCopyable?

Boost::noncopyable prevents the classes methods from accidentally using the private copy constructor. Less code with boost::noncopyable.


1 Answers

You return a value by... well returning it. However, your signature shows that you are trying to return a reference to a value. You can't do that when the object will be dropped at the end of the block because the reference would become invalid.

In your case, I'd probably write something like

#![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);
    }
}

The main thing is that the function returns a Vec<PathBuf> — a collection of a type that owns the path, and are more than just references into someone else's memory.

In your code, you do let p = val.as_path(). Here, val is a PathBuf. Then you call as_path, which is defined as: fn as_path(&self) -> &Path. This means that given a reference to a PathBuf, you can get a reference to a Path that will live as long as the PathBuf will. However, you are trying to keep that reference around longer than vec will exist, as it will be dropped at the end of the iteration.

like image 83
Shepmaster Avatar answered Sep 17 '22 13:09

Shepmaster