0

I'm trying to understand how to do some simple things in Rust, but I keep ending up fighting the borrow checker and I'm not sure why. I've written a simple function where you pass it a filename and it gives you the path to that file in your home directory.

Here's the program:

use std::env;

fn filename_in_homedir(filename: &str) -> Option<&str> {
    let mut homedir = match env::home_dir() {
        None => return None,
        Some(p) => p
    };  
    homedir.push(filename);
    homedir.to_str()
}

fn main() {
    match filename_in_homedir(".ssh/id_rsa.pub") {
        Some(s) => println!("{}", s), 
        None => println!("Oops can't get it")
    };  
}

When I try building it I get this error:

$ cargo build
   Compiling homedir-test v0.1.0 (file:///home/user/code/homedir-test)
src/main.rs:9:5: 9:12 error: `homedir` does not live long enough
src/main.rs:9     homedir.to_str()
                  ^~~~~~~
src/main.rs:3:56: 10:2 note: reference must be valid for the anonymous lifetime #1 defined on the block at 3:55...
src/main.rs:3 fn filename_in_homedir(filename: &str) -> Option<&str> {
src/main.rs:4     let mut homedir = match env::home_dir() {
src/main.rs:5         None => return None,
src/main.rs:6         Some(p) => p
src/main.rs:7     };
src/main.rs:8     homedir.push(filename);
              ...
src/main.rs:7:7: 10:2 note: ...but borrowed value is only valid for the block suffix following statement 0 at 7:6
src/main.rs: 7     };
src/main.rs: 8     homedir.push(filename);
src/main.rs: 9     homedir.to_str()
src/main.rs:10 }
error: aborting due to previous error
Could not compile `homedir-test`.

I don't understand why doesn't work. If env::home_dir() fails, the function returns None. If it succeeds, then the mutable variable homedir gets its value (which is a std::path::PathBuf). At this point, the homedir variable should be owned by the filename_in_homedir scope. The next line modifies homedir to add the filename to the end, and this works fine. The final line, calling .to_str(), returns an Option<&str> itself.

Since I'm ultimately returning a &str that's pointing somewhere inside homedir, maybe when the filename_in_homedir scope ends and homedir gets deleted, that &str does too, which is why it's throwing this error?

How do I modify this function to work properly, and what am I doing wrong?

Community
  • 1
  • 1
micah
  • 353
  • 2
  • 10

1 Answers1

2

Since I'm ultimately returning a &str that's pointing somewhere inside homedir, maybe when the filename_in_homedir scope ends and homedir gets deleted, that &str does too, which is why it's throwing this error?

That's correct: you're trying to return a pointer to a value owned by filename_in_homedir's stack frame, which will be dropped once it returns, invalidating the pointer. You need to return a String, rather than a &str. Here's one way to do it:

use std::env;
use std::borrow::ToOwned;

fn filename_in_homedir(filename: &str) -> Option<String> {
    let mut homedir = match env::home_dir() {
        None => return None,
        Some(p) => p
    };  
    homedir.push(filename);
    homedir.to_str().map(ToOwned::to_owned)
}

fn main() {
    match filename_in_homedir(".ssh/id_rsa.pub") {
        Some(s) => println!("{}", s), 
        None => println!("Oops can't get it")
    };
}
Francis Gagné
  • 60,274
  • 7
  • 180
  • 155
  • Thank you, this works great! I think I should read up on `std::borrow`, it seems like I'll need to use it often. – micah Apr 24 '16 at 19:48