使用强制转换的可变访问器安全吗?

2024-05-25

我试图理解重复代码的问题& and &mut在 getter 类型的函数中。我试图了解这个问题的特定解决方案是否使用内部的强制转换unsafe块将是安全的。

以下是该问题的示例。它取自非常好的教程 使用太多的链表学习 Rust https://rust-unofficial.github.io/too-many-lists/second-final.html.

type Link<T> = Option<Box<Node<T>>>;

pub struct List<T> {
    head: Link<T>,
}

struct Node<T> {
    elem: T,
    next: Link<T>,
}

impl<T> List<T> {
    // Other methods left out...

    // The implementation of peek is simple, but still long enough
    // that you'd like to avoid duplicating it if that is possible.
    // Some other getter-type functions could be much more complex
    // so that you'd want to avoid duplication even more.
    pub fn peek(&self) -> Option<&T> {
        self.head.as_ref().map(|node| {
            &node.elem
        })
    }

    // Exact duplicate of `peek`, except for the types
    pub fn peek_mut(&mut self) -> Option<&mut T> {
        self.head.as_mut().map(|node| {
            &mut node.elem
        })
    }
}

解决方案

在我看来,你可以在unsafe块来解决这个问题。该解决方案似乎具有以下属性:

  • 它可以以安全的方式完成。
  • 它为不安全的实现提供了一个安全的接口。
  • 实现很简单。
  • 它消除了代码重复。

下面是解决方法:

// Implemention of peek_mut by casting return value of `peek()`
pub fn peek_mut(&mut self) -> Option<&mut T> {
    unsafe {
        std::mem::transmute(self.peek())
    }
}

以下是我认为它看起来安全的理由:

  1. 返回值peek()来自具有已知别名情况的已知来源。
  2. 由于参数类型是&mut self没有对其元素的引用。
  3. 因此返回值是peek()在非别名中。
  4. 返回值peek()不会逃逸这个函数体。
  5. 投射未锯齿的& to &mut似乎没有违反指针别名规则。
  6. 演员表的目标和源的生命周期彼此匹配。

杂项注释

  • 同样的问题讨论以下问题:如何避免在 Rust 中为可变和不可变引用编写重复的访问器函数? https://stackoverflow.com/questions/41436525/how-to-avoid-writing-duplicate-accessor-functions-for-mutable-and-immutable-refe

    这个问题有所不同,因为它询问问题的一种特定解决方案的细节。

  • 该问题还有其他几种解决方案,例如:Rust 中的可变性抽象 https://lab.whitequark.org/notes/2016-12-13/abstracting-over-mutability-in-rust/

    但所有其他解决方案似乎都会给代码带来相当多的额外复杂性。

  • The Nomicon https://doc.rust-lang.org/nomicon/transmutes.html强烈主张强制转换始终是未定义的行为& to &mut,这意味着该解决方案不安全。但它没有试图解释原因。

  • Rust 参考 https://doc.rust-lang.org/reference/behavior-considered-undefined.html指出“打破指针别名规则”是 UB。在我看来,由于上述原因,这个解决方案并没有做到这一点。


问题

我对 Rust 知识比我更深入的人有以下问题:

  • 是否提供了实现peek_mut safe?
  • 是否还有其他必要的论据需要证明它是安全的,但我错过了?
  • 如果确实不安全,为什么呢?你能给出详细的解释吗?
  • 在这种情况下,是否有类似的安全解决方案?

我相信这段代码会调用未定义的行为。去引用诺米康 https://doc.rust-lang.org/nomicon/transmutes.html:

  • Transmuting an & to &mut is UB
    • 将 & 转换为 &mut 始终是 UB
    • 不,你做不到
    • 不,你并不特别

更重要的是,Rust 编译器将标记返回值peek()在 LLVM 中间表示中是不可变的,并且 LLVM 可以根据此断言自由进行优化。在这种特定情况下,目前可能不会发生,但我仍然认为这是未定义的行为。如果您想不惜一切代价避免重复,可以使用宏。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用强制转换的可变访问器安全吗? 的相关文章

随机推荐