重构会破坏可变借用 - 为什么?

3
我是一个有用的助手,可以为您翻译文本。

我试图理解为什么以下重构会导致错误,即使它应该有效地具有相同的行为:

之前:

fn req_handler(req: &mut Request) -> IronResult<Response> {
    let pool = req.get::<Read<Database>>().ok().expect("database component not initialised");
    let connection = pool.get().unwrap();

    let maybe_id = req.extensions.get::<Router>().unwrap().find("id");
    ...

之后:

pub fn get_pool_connection<'a, 'b, 'c>(req: &'a mut Request<'b, 'c>) -> PooledConnection<'a, PostgresConnectionManager> {
    let pool = req.get_ref::<Read<Database>>().ok().expect("database component not initialised");
    pool.get().unwrap()
}
fn req_handler(req: &mut Request) -> IronResult<Response> {
    let connection = get_pool_connection(req);
    let maybe_id = req.extensions.get::<Router>().unwrap().find("id");

这会导致错误:
src/main.rs:64:20: 64:34 error: cannot borrow `req.extensions` as immutable because `*req` is also borrowed as mutable
src/main.rs:64     let maybe_id = req.extensions.get::<Router>().unwrap().find("id");
                                  ^~~~~~~~~~~~~~
src/main.rs:62:42: 62:45 note: previous borrow of `*req` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `*req` until the borrow ends
src/main.rs:62     let connection = get_pool_connection(req);
                                                        ^~~
src/main.rs:76:2: 76:2 note: previous borrow ends here
src/main.rs:61 fn req_handler(req: &mut Request) -> IronResult<Response> {
...
src/main.rs:76 }

所以问题在于get_pool_connection借用了请求并返回connection,这阻止了进一步使用req。但为什么会发生这种情况?req的使用寿命至少与返回的PooledConnection相同。它也没有被移动,只是作为&mut传递。那么是什么阻止了请求的使用?
为什么错误提示说*req被借用了,当本地的req和函数参数都是引用时?
(相关文档:RequestPool

1
错误表明 *req 被借用了,这是因为 req 是一个引用。毕竟,通常您会借用被引用指向的某些拥有数据,而不是引用本身。 - Vladimir Matveev
1个回答

2

这实际上正是生命周期注解的含义。如果您有一个具有此原型的函数:

fn get_bar<'a>(&'a Foo) -> Bar<'a> { ... }

这意味着返回的Bar对象的生命周期与Foo对象的生命周期相绑定。因此:
  • Bar对象只要Foo对象还活着,就会借用它。
  • Bar对象不允许超越Foo对象的生命周期。
在你的情况下,connection的类型为PooledConnection<'a, ...>,其中'a&'a mut req中定义的生命周期,因此被视为对req的可变借用。
在重构之前,它可以正常工作,因为connection的生命周期实际上与pool的生命周期相关联,而pool并没有借用req,因为它没有持有任何生命周期参数。
由于您的重构迫使connection借用req,这在以前是不需要的,因此可能不是一个合适的重构。

有没有办法返回连接并使重构工作?还是在这种情况下我也必须返回池? - viraptor
1
你会很难同时返回池和连接,因为它们的生命周期是相互关联的。在这种将仅两行代码重构为函数的情况下,我没有看到比不重构更好的解决方案。 - Levans

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接