首页 > 解决方案 > 这种使用不安全的琐碎安全吗?

问题描述

我遇到了一个 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()
}

锈游乐场

第一个问题:我是否认为这段代码是 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)
    }
}

第二个问题:这是微不足道的安全吗?我以前从未使用过 unsafe,所以我很紧张。

我想第三个问题:有没有办法用安全的 Rust 重写它?约束是value应该只在非变异路径上调用一次。

标签: rustunsafeborrow-checker

解决方案


编译器不允许您的代码的原因是因为 ref_to_value 的生命周期必须至少与 increment_if_odd 的生命周期一样长才能返回。

如果您添加回省略的生命周期,则 ref_to_value 必须具有生命周期'a。我的理解是编译器不能改变引用的生命周期。编写安全 rust 来解决此问题的一种方法是使 ref_to_value 可变,并修改 Thing::increment。

您的不安全代码所做的是允许编译器为 ref_to_value 提供更短的生命周期,以及通过强制转换指针生命周期'a 创建的新引用。我认为你的不安全代码是“安全的”,因为没有任何 rust 的借用规则被破坏,而且我们知道新的引用不会比数据更有效。

struct Thing {
    value: i32
}

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

/// Increments the value of `thing` if it is odd, and returns a reference to the value.
fn increment_if_odd<'a>(thing: &'a mut Thing) -> &'a i32 {
    
    let ref_to_value : &'a mut i32 = thing.mut_value();
    if (*ref_to_value % 2) != 0 {
        Thing::increment(ref_to_value);
    }
    ref_to_value
}

推荐阅读