我有两个struct:
struct A {
map: HashMap<u32, Vec<B>>,
}
struct B {
weak: Weak<A>
}
当A
被构造时,它将拥有几个B
,每个A
都链接到刚刚构造的A
,类似于这样:
let a = Arc::new(A { map: HashMap::new() });
let b1 = B { weak: Arc::downgrade(&a) };
let b3 = B { weak: Arc::downgrade(&a) };
let b2 = B { weak: Arc::downgrade(&a) };
a.map.insert(5, vec![b1, b2]);
a.map.insert(10, vec![b3]);
Playground
由于Arc
不提供修改Map的方法,因此这不起作用。Arc::get_mut
不起作用,因为Weak
已经构造为该值。
如何用一些B
构造一个A
?在访问map
时,我尽量避免运行时检查,因为在构建之后,它将永远不会被再次修改。我没有问题使用不安全的代码或批准的夜间功能。
3条答案
按热度按时间r7s23pms1#
如果你已经有了
Weak
引用,Arc::get_mut()
也会失败,所以你需要考虑使用 interior mutability。由于您使用的是Arc
,因此我假设您处于多线程环境中,因此我将使用线程安全的RwLock
。现在你可以像这样构造这些对象:
如果你真的想摆脱
Mutex
的所有运行时开销,并且你不介意使用unsafe
代码,你可以使用UnsafeCell
。它的开销为零,但它的接口需要一个unsafe
块,这是代码中额外的一层解包。另外,UnsafeCell
不是Sync
,因此无法在线程之间共享它。为了解决这些问题,通过确保在构建过程中只需要考虑
UnsafeCell
,您可以利用UnsafeCell
具有零大小成本并且不影响布局的事实。代替A
,使用不同的类型进行构造,除了UnsafeCell
之外,它与A
相同。这些类型可以与mem::transmute
互换使用。你也可以使用原始指针来实现这一点,但我觉得使用
UnsafeCell
会更“安全”一点!LLVM在保证某些数据不可变时会做一些优化,而UnsafeCell
在违反这些保证时会做一些魔法来保护您。所以我不能100%确定这样做的安全性:6qfn3psc2#
实际上,我会从相反的方向来处理。
HashMap
是一个比Weak<T: Sized>
复杂得多的类型,因此事后交换Weak
要容易得多。因此,我的方法是:1.使用虚拟引用创建
B
。1.创建
A
,转移B
的所有权。1.迭代
B
,将A
替换为真实的的A
。AFAIK,标准库不提供任何方法来(1)创建null
Weak
和(2)原子交换它们。Crossbeam有一个ArcCell
的例子:只需搜索/替换所有Arc
就可以得到一个WeakCell
!我们可以使用
WeakCell<T>
:你可以在on the playground中看到。
应该可以修改
WeakCell
以从0
构造它(保证它稍后会被初始化),从而避免对伪引用的需要。这是留给读者的练习)wqlqzqxt3#
这是一个老问题,但我有类似的dillema,显然有一个比UnsafeCell更好的解决方案,如果你真的不想要互斥(像我一样)。
Rust 1.60有
Arc::new_cyclic
(https://doc.rust-lang.org/std/sync/struct.Arc.html#method.new_cyclic),它以闭包作为参数:该解决方案:
unsafe
Mutex
Weak
。