unsafe 的这种使用是非常安全的吗?

Jam*_*ell 15 unsafe rust borrow-checker

我遇到了 Rust 借用检查器错误,我认为这是当前非词法生命周期实现的限制。我想写的代码看起来像这样:

struct Thing {
    value: i32
}

impl Thing {
    fn value(&self) -> &i32 {
        &self.value
    }
    fn increment(&mut self) {
        self.value += 1;
    }
}

/// Increments the value of `thing` if it is odd, and returns a reference to the value.
fn increment_if_odd(thing: &mut Thing) -> &i32 {
    let ref_to_value = thing.value();
    if (*ref_to_value % 2) == 0 {
        return ref_to_value;
    }
    thing.increment();  // fails to compile because the immutable borrow `ref_to_value` is still alive
    thing.value()
}
Run Code Online (Sandbox Code Playgroud)

铁锈游乐场

第一个问题:我认为这段代码是 100% 安全并且借用检查器过于保守的想法是否正确?返回的分支ref_to_value不会发生变化thing,因此保证引用有效,而另一个分支ref_to_value根本不使用。(我知道如果我return ref_to_value;return thing.value();它替换就会编译,但在我的实际代码中该value方法很昂贵。)

看来我可以通过指针“清洗”引用来解决这个问题:

if (*ref_to_value % 2) == 0 {
    return unsafe {
        &*(ref_to_value as *const i32)
    }
}
Run Code Online (Sandbox Code Playgroud)

第二个问题:这安全吗?我以前从未使用过 unsafe 所以我很紧张。

我猜第三个问题:有没有办法用安全的 Rust 重写它?限制是value只能在非变异路径上调用一次。

kal*_*ave -4

可能是安全的,还没仔细观察过。另一方面,您可以将该函数重写为:

fn increment_if_odd(thing: &mut Thing) -> &i32 {
    if (*thing.value() % 2) != 0 {
        thing.increment();
    }
    thing.value()
}
Run Code Online (Sandbox Code Playgroud)