Follow-up of Why doesn't Weak::new() work when Rc::downgrade() does?
当试图以一种不需要它为底层类型分配内存的方式实现Weak::new()
时,即使它永远不会被使用,我遇到了一个障碍.
RcBox<T>
的定义相当简单:
struct RcBox<T: ?Sized> {
strong: Cell<usize>,
weak: Cell<usize>,
value: T,
}
这里的目标是创建一个RcBox<T>
,实际上不包含任何value
.基本上是RcBox<()>
.
然而,有一个障碍.*mut RcBox<()>
是一个细指针,但*mut RcBox<T>
可能是一个胖指针.我们有这个fat指针的数据部分,但是fat指针有很多不同的情况,所以try 合成其余的是hard.
从链接的问题中可以看出,我可以让它只适用于trait 对象:
impl<T: ?Sized> Weak<T> {
pub fn new() -> Weak<T> {
unsafe {
let boxed = Box::into_raw(box RcBox {
strong: Cell::new(0),
weak: Cell::new(1),
value: (),
});
let ptr = if size_of::<*mut ()>() == size_of::<*mut T>() {
let ptr: *mut RcBox<T> = transmute_copy(&boxed);
ptr
} else {
let ptr: *mut RcBox<T> = transmute_copy(&TraitObject {
data: boxed as *mut (),
vtable: null_mut(),
});
ptr
};
Weak { ptr: Shared::new(ptr) }
}
}
}
然而,这将不适用于str
(例如).
我再次try 分离RcBox
的固定大小部分,同时让编译器推断指针的胖部分:
struct RcBox<T: ?Sized> {
counters: RcBoxCounters<T>,
value: T,
}
struct RcBoxCounters<T: ?Sized> {
strong: Cell<usize>,
weak: Cell<usize>,
_phantom: PhantomData<T>,
}
impl<T: ?Sized> Weak<T> {
pub fn new() -> Weak<T> {
unsafe {
let boxed = Box::into_raw(box RcBox::Counters::new(0, 1));
Weak { ptr: Shared::new(boxed as *mut RcBox<T>) }
}
}
}
这听起来很聪明,直到编译器扼杀了你的热情:
error[E0375]: implementing the trait `CoerceUnsized` requires multiple coercions --> <anon>:58:40 | 58 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<RcBox<U>> for RcBox<T> {} | ^^^^^^^^^^^^^^^^^^^^^^^ requires multiple coercions | = note: `CoerceUnsized` may only be implemented for a coercion between structures with one field being coerced = note: currently, 2 fields need coercions: counters (RcBoxCounters<T> to RcBoxCounters<U>), value (T to U)
也就是说:
- 我认为要让编译器合成脂肪部分,我需要一个
PhantomData
分的RcBoxCounters
, - 但是,这样做需要强制进行2次转换,这是不允许的.
那么,有没有办法修复Weak::new()
,使其停止分配无关(不必要)的内存?
Note: I do mean allocating only space for the two counters, allocating large and trimming afterward does NOT help.
Note: It has been remarked that one could use an 100 or special value to denote the absence of value. This requires branching on each method, which may not be desirable. I prefer learning to fiddle with fat pointers.