How do I return a &Path from a function?

2019-04-29 05:26发布

问题:

I'm trying to understand how to write proper Rust code, but I think I may be overestimating the power of the compiler's ability to understand the lifetimes of my objects. This is the code as I expected it to work:

use std::path::Path;
use std::env;
use rusqlite::SqliteConnection;

struct SomeDatabase {
    conn: SqliteConnection,
}

impl SomeDatabase {
    fn getPath() -> &Path {
        let path = env::home_dir().unwrap();
        path.push("foo.sqlite3");
        path.as_path()
    }

    fn open() -> SomeDatabase {
        let path = SomeDatabase::getPath()
        SomeDatabase { conn: SqliteConnection::open(path).unwrap() }
    }
}

fn main() {
    let db = SomeDatabase::open();
}

When I try to compile this, I get an error about a missing lifetime specifier on &Path. I know if this took a reference parameter from the caller, it would take on the same lifetime as that reference has. Here though what I was expecting is that the lifetime would be attached to the variable I am assigning the result to.

I know lifetimes can be added explicitly, but I don't know how to apply them in this case. The compiler suggests trying the 'static lifetime, but that doesn't make sense here as far as I know because the source of this function's return value isn't static.

Now, just to try to see what happened if I tried to compile the rest of the code, I changed the return type from &Path to PathBuf and called as_path() in open(). This caused the compiler to output these errors:

src\main.rs:22:30: 22:52 error: the trait `core::marker::Sized` is not implemented for the type `[u8]` [E0277]
src\main.rs:22         SomeDatabase { conn: SqliteConnection::open(path).unwrap() }
                                            ^~~~~~~~~~~~~~~~~~~~~~
src\main.rs:22:30: 22:52 note: `[u8]` does not have a constant size known at compile-time
src\main.rs:22         SomeDatabase { conn: SqliteConnection::open(path).unwrap() }
                                            ^~~~~~~~~~~~~~~~~~~~~~

SqliteConnection::open() returns a Result<SqliteConnection, SqliteError> and the only field inside SqliteConnection is a RefCell, so I don't understand where this error about a byte array is coming from.

So, why aren't things working as I expect and what is the most Rusty way to write this code?

回答1:

In your first case, you are creating a value and then trying to return a reference to it. But since you aren't storing that value anywhere, it gets destroyed after the function ends. If it was allowed, it'd be a use-after-free bug.

The reason it suggested returning a &'static Path is because the function isn't parameterized over any lifetimes, so the only lifetime you can be sure outlives anything that wants to use the return value would be 'static.

You are correct that you need to return a PathBuf directly instead of an &Path.

I'm not quite sure why you are getting the [u8] sized errors.

You don't need to call "as_path()" at all. SqliteConnection::open takes a value that implements AsRef<Path> (AsRef is sort of like Into), and PathBuf does implement that trait.



标签: rust