【问题标题】:How do I return a &Path from a function?如何从函数返回 &Path?
【发布时间】:2015-09-06 01:44:11
【问题描述】:

我试图了解如何编写正确的 Rust 代码,但我认为我可能高估了编译器理解对象生命周期的能力。这是我期望它工作的代码:

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

当我尝试编译它时,我收到关于 &Path 上缺少生命周期说明符的错误。我知道如果这从调用者那里获取了一个引用参数,它将与该引用具有相同的生命周期。在这里,虽然我期望的是生命周期将附加到我将结果分配给的变量。

我知道可以显式添加生命周期,但我不知道在这种情况下如何应用它们。编译器建议尝试 'static 生命周期,但据我所知,这在这里没有意义,因为此函数返回值的来源不是静态的。

现在,只是想看看如果我尝试编译其余代码会发生什么,我将返回类型从 &Path 更改为 PathBuf 并在 open() 中调用 as_path()。这导致编译器输出这些错误:

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() 返回一个Result<SqliteConnection, SqliteError>,而SqliteConnection 中的唯一字段是RefCell,所以我不明白这个关于字节数组的错误来自哪里。

那么,为什么事情没有按我的预期工作,编写这段代码最生锈的方式是什么?

【问题讨论】:

    标签: rust


    【解决方案1】:

    在您的第一种情况下,您正在创建一个值,然后尝试返回对它的引用。但是由于您没有将该值存储在任何地方,因此它会在函数结束后被销毁。如果允许,那将是一个use-after-free 的错误。

    它建议返回 &'static Path 的原因是因为该函数在任何生命周期内都没有参数化,因此您可以确定的唯一生命周期比任何想要使用返回值的生命周期都长的是 'static

    您是正确的,您需要直接返回 PathBuf 而不是 &Path

    我不太确定您为什么会收到 [u8] 大小的错误。

    您根本不需要调用“as_path()”。 SqliteConnection::open 采用实现 AsRef<Path> 的值(AsRef 有点像 Into),而 PathBuf 确实实现了该特征。

    【讨论】:

    • 我意识到,在第一种情况下,我返回的是对在函数内部创建的东西的引用,但是如果我将 getPath() 设为 SomeDatabase 的成员函数,编译器就足够聪明,可以保存 &Path直到 SomeDatabase 的实例超出范围。我在问是否有办法告诉编译器引用的生命周期属于调用 getPath() 的函数(在 getPath() 是关联函数的情况下)。它的生命周期代码应该是直到持有引用的变量超出 open() 的范围。
    • 还必须补充一点,如果我离开 as_path(),我会得到一个不匹配的类型错误:“expected '&_', found 'std::path::PathBuf' (expected &-ptr,找到 struct 'std::path::PathBuf')
    • @AustinWagner 变量path 定义在getPath() 方法的主体中,除非您返回它,否则它的生命周期在此函数主体范围的末尾处结束。没有办法避免这条规则。在您的情况下,除了为 getPath() 函数返回 PathBuf 之外别无选择。
    • @AustinWagner 如果我将 getPath 设为成员函数 [...] 编译器会保留 [s] &Path 直到实例 [...] 超出范围 —这是simply not true。你有同样的问题,当函数返回时 局部变量 被删除,因为没有人拥有它。
    • SqliteConnection::open 采用实现的值 — 请注意,it actually takes 引用实现了AsRef 的类型。我认为这可能是发生大小错误的原因。对于图书馆而言,这似乎是一个奇怪的决定......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多