如何安全地存储通用值的不可变别名副本?

Vee*_*rac 6 generics traits type-safety rust

摘要

出于优化目的,我希望创建一个数据结构,将单个值存储在多个不同的位置.数据结构只允许通过不可变引用使用这些值,或者完全从数据结构中删除它们.

如何确保这对于通用类型是安全的?

简化示例

为上下文提供一个微不足道(但有点不切实际)的示例,请考虑一个缓存最近使用的值的切片:

struct Pair<'a> {
    values: &'a [T],
    last_accessed: &'a T,
}
Run Code Online (Sandbox Code Playgroud)

但是,访问最后访问的元素仍然会引发指针取消引用,因此代码需要按值缓存:

struct Pair<'a> {
    values: &'a [T],
    last_accessed: NoDrop<T>,
}
Run Code Online (Sandbox Code Playgroud)

在大多数情况下,这似乎是安全的.例如,如果T是a u32,则缓存只是数据的简单副本.

即使T是a Vec<U>,这似乎是安全的,因为任何访问&last_accessed都不能改变向量的任何直接成员.堆分配是可传递的,不会重复,因此没有明显的别名问题.

为什么这么难?

这对所有值都不安全.包含a的值Cell可以调用内部可变性并最终违反内部约束,这些约束在通过未传播该更改的值进行访问时会导致不安全行为.

问题是我可以对T可以使其安全的通用设施施加什么约束?据我所知,我只需要UnsafeCell通过指针除外.

怎么样T: Copy

T: Copy 看起来像是一个不错的解决方案,但有两个主要缺点:

  • "没有绝对的根本原因,为什么[ UnsafeCell]没有实现Copy" - 亚历克斯克里奇顿.这个Copy要求似乎是巧合,而不是保证.

  • Copy禁止太多; Vec不是,Copy但不需要被禁止.

关于什么 T: Sync

Sync 因为,接近正确的想法

不属于类型Sync是那些具有"室内易变性"在非线程安全的方式,比如CellRefCellstd::cell.

但是,有几个类型,如原子能,有内部的可变性这线程安全的.

Vee*_*rac 6

您可以UnsafeCell使用自动特征强制执行约束.这些是默认定义的,但可以选择使用特定类型的特殊语法 - 您只想选择退出UnsafeCell.

这些以前称为Opt-In内置特征(OIBIT),但由于它们既不是选择性的也不是内置的,因此被重命名,并且实际上是可以在普通用户代码中定义的选择性特征.

首先启用它,创建特征并默认实现.这使用了一些神奇的语法.

#![feature(optin_builtin_traits)]

pub unsafe trait CopyRef {}
unsafe impl CopyRef for .. {}
Run Code Online (Sandbox Code Playgroud)

然后你选择退出UnsafeCell.

// Opt out of interior mutability
impl<T: ?Sized> !CopyRef for UnsafeCell<T> {}
Run Code Online (Sandbox Code Playgroud)

然后,您将需要重新启用UnsafeCell后面的指针和PhantomData.

use std::marker::PhantomData;
use std::cell::UnsafeCell;

// Opt in for indirect interior mutability
unsafe impl<'a, T: ?Sized> CopyRef for *const T {}
unsafe impl<'a, T: ?Sized> CopyRef for *mut T {}
unsafe impl<'a, T: ?Sized> CopyRef for &'a T {}
unsafe impl<'a, T: ?Sized> CopyRef for &'a mut T {}

// Box is special and needs its own opt-in
unsafe impl<T: ?Sized> CopyRef for Box<T> {}

// And fake interior mutability
unsafe impl<T: ?Sized> CopyRef for PhantomData<T> {}
Run Code Online (Sandbox Code Playgroud)

瞧.您自己的用户定义选择退出Opt-In内置自动特征.