为什么在函数调用完成后,可变借用的生命周期不会结束?

问题描述 投票:3回答:1

我正在为halite.io编写一个机器人,并且在理解借用的一些影响方面遇到了问题。这是无法编译的代码:

let scanLoc = hlt::types::Location {
    x: oflow(coord.0 + l.x as i32, game_map.width),
    y: oflow(coord.1 + l.y as i32, game_map.width),
};
let scan = game_map.get_site(scanLoc, types::STILL);
if (&scan.owner != id) | (scan.owner != 0u8) {
    let ang = game_map.get_angle(l, scanLoc);
    debug!("angle b/w: {}", ang);
    return (l, 2);
}

这是编译器错误:

error[E0502]: cannot borrow `*game_map` as immutable because it is also borrowed as mutable
   --> src/MyBot.rs:112:27
      |
  110 |             let scan = game_map.get_site(scanLoc, types::STILL);
      |                        -------- mutable borrow occurs here
  111 |             if (&scan.owner != id) | (scan.owner != 0u8) {
  112 |                 let ang = game_map.get_angle(l, scanLoc);
      |                           ^^^^^^^^ immutable borrow occurs here
  ...
  116 |         }
      |         - mutable borrow ends here

这是GameMap函数和结构的代码:

#[derive(Clone, Debug)]
pub struct GameMap {
    pub width: u16, // Number of columns.
    pub height: u16, // Number of rows.
    pub contents: Vec<Vec<Site>>,
}

impl GameMap {
    pub fn in_bounds(&self, l: Location) -> bool {
        // ...
    }
    pub fn get_distance(&self, l1: Location, l2: Location) -> u16 {
        // ...
    }
    pub fn get_angle(&self, l1: Location, l2: Location) -> f64 {
        // ...
    }
    pub fn get_location(&self, l: Location, d: u8) -> Location {
        // ...
    }
    pub fn get_site(&mut self, l: Location, d: u8) -> &mut Site {
        // ...
    }
}

为什么Rust可以相互借用函数,即使它是借用函数,它也不会在返回结果时返回借位(结束生命周期),所以之后可以借用它?

rust
1个回答
13
投票

编者注:这个具体问题已经通过引入non-lexical lifetimes解决了。

让我们来看一个微小的再现:

struct Site {
    owner: u8,
}

struct GameMap {
    site: Site,
}

impl GameMap {
    fn do_anything(&self) {}

    fn get_site(&mut self) -> &mut Site {
        &mut self.site
    }
}

fn main() {
    let mut game_map = GameMap {
        site: Site { owner: 0 },
    };
    let site = game_map.get_site();
    game_map.do_anything();
}
error[E0502]: cannot borrow `game_map` as immutable because it is also borrowed as mutable
  --> src/main.rs:22:5
   |
21 |     let site = game_map.get_site();
   |                -------- mutable borrow occurs here
22 |     game_map.do_anything(); // Compiler error!
   |     ^^^^^^^^ immutable borrow occurs here
23 | }
   | - mutable borrow ends here

我们的GameMap只拥有一个Site,但这就足够了。对get_site的调用返回一个引用(在这种情况下,它恰好是可变的):

fn get_site(&mut self) -> &mut Site

感谢lifetime elision,这是一样的

fn get_site<'a>(&'a mut self) -> &'a mut Site

这意味着允许返回的引用指向GameMap内部的内容(它确实如此)。然后我们将该引用保存在变量中 - site

这意味着我们不能再使用对game_map的任何不可变引用,因为它们可能已经(或将来会被)通过可变引用对地图所做的更改失效:

  • 在任何给定时间,您可以拥有一个可变引用或任意数量的不可变引用。
  • 引用必须始终有效。

- The Rust Programming Language chapter on references and borrowing

为什么Rust可以相互借用函数,即使它是借用函数,它也不会在返回结果时返回借位(结束生命周期),所以之后可以借用它?

Rust可变地借用你的结构,因为你正在调用一个需要可变引用的方法(&mut self)。然后该方法返回一个可变引用,将结构的借位转移到返回的值。当返回的值超出范围时,借用结束。


那么,你如何解决它?可能最灵活的解决方案是引入限制可变借用的范围:

let zhu_li_do_the_thing = {
    let site = game_map.get_site();
    site.owner == 5 || site.owner == 42
};

if zhu_li_do_the_thing {
    game_map.do_anything();
}

另一个是相同的想法,但要求你永远不要将借入存储在一个变量中。因此,可变借款不会超出该陈述:

if game_map.get_site().owner == 42 {
    game_map.do_anything();
}

对于惯用的Rust代码来说,有一个方法的foofoo_mut变体是常见的,因为当你不需要可变性时。如果你需要改变game_mapsite的不可变借款尚未兑现,这可能无济于事。

fn get_site(&self) -> &Site {
    &self.site
}

fn get_site_mut(&mut self) -> &mut Site {
    &mut self.site
}
let site = game_map.get_site();
if site.owner == 5 || site.owner == 42 {
    game_map.do_anything();
}

也可以看看:

© www.soinside.com 2019 - 2024. All rights reserved.