rust - 使用强制转换的可变访问器是否安全?

标签 rust unsafe

我试图了解 & 的重复代码的问题和 &mut在 getter 类型的函数中。我试图了解是否使用 unsafe 中的强制转换来解决此问题的特定解决方案 block 将是安全的。

以下是该问题的一个示例。它取自非常好的教程 Learning Rust With Entirely Too Many Linked Lists .

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 中使用类型转换。 block 来解决这个问题。该解决方案似乎具有以下属性:
  • 它可以以安全的方式完成。
  • 它为不安全的实现提供了一个安全的接口(interface)。
  • 实现很简单。
  • 它删除了代码重复。

  • 以下是解决方案:
    // 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())
        }
    }
    

    这些是为什么我认为它似乎安全的论点:
  • peek()的返回值来自具有已知别名情况的已知来源。
  • 由于参数类型是&mut self它的元素没有引用。
  • 因此 peek() 的返回值在无别名。
  • peek()的返回值不会转义这个函数体。
  • 转换无别名 &&mut似乎没有违反指针别名规则。
  • 转换的目标和源的生命周期彼此匹配。


  • 杂项说明
  • 以下问题讨论了相同的问题:How to avoid writing duplicate accessor functions for mutable and immutable references in Rust?

    这个问题是不同的,因为它询问问题的一个特定解决方案的细节。
  • 该问题还有其他类型的解决方案,例如:Abstracting over mutability in Rust

    但是所有其他解决方案似乎都给代码带来了相当多的额外复杂性。
  • Nomicon强烈断言强制转换 & 始终是未定义的行为。至&mut ,这意味着该解决方案不安全。但它没有试图解释原因。
  • The Rust Reference声明“打破指针别名规则”是UB。对我来说,由于上面给出的原因,这个解决方案似乎没有做到这一点。


  • 问题

    对于 Rust 知识比我自己更深的人,我有以下问题:
  • peek_mut 的提供的实现吗?安全的?
  • 是否需要其他必要的论据来确定它是安全的,我错过了?
  • 如果确实不安全,那又是为什么呢?你能给出详细的解释吗?
  • 在那种情况下,是否有类似的安全问题解决方案?
  • 最佳答案

    我相信这段代码会调用未定义的行为。报价 the Nomicon :

    • Transmuting an & to &mut is UB
      • Transmuting an & to &mut is always UB
      • No you can't do it
      • No you're not special


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

    关于rust - 使用强制转换的可变访问器是否安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59594870/

    相关文章:

    c# - .NET Micro Framework 不安全代码

    rust - 为什么 Pin::map_unchecked 不安全?

    http - 如何避免http响应中的点文件或点目录

    rust - 我应该从 getter 返回 &Option<Foo> 还是 Option<&Foo> ?

    rust - 错误 : `line` does not live long enough but it's ok in playground

    rust - 如何获得rust-sdl2窗口表面并同时使用事件迭代器?

    c# 为什么 "unsafe"超出应用程序地址范围

    macros - 修复 "no rules expected the token"宏错误

    rust - 如何使用 serde 和 bincode 映射填充超过 32 字节的 C 结构?

    java - 在 Java 9 中还可以使用 sun.misc.Unsafe 吗?