简体   繁体   中英

How to return a struct by value in Rust?

I'm learning Rust and failed on the first steps already... I have the next function:

use std::env;
use std::path::Path;

fn get_path() -> Path {
    let args: Vec<String> = env::args().collect();
    assert!(!args.is_empty(), "Target path is required!");
    let path = Path::new(&args[0]);
    assert!(path.exists(), "Target path doesn't exist!");
    assert!(path.is_dir(), "Target path is not a directory!");
    return path;
}

It's very simple function but path is a reference and I can't get how can I return Path object from function by value? Or how to return a reference that will be alive in outer function context?

PS I looked for similar question but I didn't get it, unfortunately.

Strictly speaking, Path is not a reference, it is rather an unsized type which can only exist behind a reference, and indeed Path::new returns &Path not Path . Which is therefore incompatible with the -> Path you've annotated your function with.

These are in fact the two things the compile error tells you, and you really want to give people compilation errors (or reproduction cases) when posting Rust code as the errors are extremely informative once you've gotten used to them:

error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
 --> src/lib.rs:4:18
  |
4 | fn get_path() -> Path {
  |                  ^^^^ borrow the `Path` instead
  |

says that you're returning an unsized type which is not allowed), and

error[E0308]: mismatched types
  --> src/lib.rs:10:12
   |
4  | fn get_path() -> Path {
   |                  ---- expected `std::path::Path` because of return type
...
10 |     return path;
   |            ^^^^ expected struct `std::path::Path`, found `&std::path::Path`

explains that the type you want to return doesn't match the type of the value you're returning.

Anyway as the official documentation for Path notes , the owned / structure version of Path is PathBuf , so you should return that, and convert your Path into a PathBuf , or really just create a PathBuf in the first place, eg

use std::env;
use std::path::PathBuf;

fn get_path() -> PathBuf {
    let args: Vec<String> = env::args().collect();
    assert!(!args.is_empty(), "Target path is required!");
    let path = PathBuf::from(&args[0]);
    assert!(path.exists(), "Target path doesn't exist!");
    assert!(path.is_dir(), "Target path is not a directory!");
    path
}

Incidentally,

 Path::new(&args[0]);

is probably not what you expect or want: as the documentation for std::env::args notes:

The first element is traditionally the path of the executable

and this is not an area where Rust has seen fit to diverge from the underlying system.

You likely want args[1] instead, or to use a higher-level args-parsing API.

And an other aside related to Sven Marnach's comment to your question: calling path.exists then path.is_dir requires fetching the metadata twice (I don't think Rust caches this information). The efficienty aspect is probably not primordial here but you may still want to explicitly use Path::metadata , followed by asking that if is_dir ( Path::metadata will return an Err if the path is not to a valid on-disk thing).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM