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.

推荐答案

是的,有一种方法,它实际上被提交到了标准库:

这一变化使得Weak::new()完全不分配内存.相反,它是用空指针创建的.对弱者所做的唯一事情就是try 升级、克隆和删除,这意味着代码实际上很少需要判断指针是否为空.

Rust相关问答推荐

交叉术语未正确清除屏幕

如何装箱生命周期相关联的两个对象?

制作一片连续整数的惯用Rust 方法?

有没有办法在Rust中配置常量变量的值?

原始数组数据类型的默认trait实现

为什么这是&q;,而让&q;循环是无限循环?

类型批注需要静态生存期

我是否可以在Ruust中修改 struct 实例上的字符串,以使其在修改后具有相同的字符串生存期?

try 创建随机数以常量

关于使用平面图功能的borrow 判断器的问题

为什么RefCell没有与常规引用相同的作用域?

为什么不';t(&;mut-iter).take(n)取得iter的所有权?

`use` 和 `crate` 关键字在 Rust 项目中效果不佳

Rust中是否可以在不复制的情况下从另一个不可变向量创建不可变向量?

在 Rust 中使用 `SecTrustSettingsSetTrustSettings` 绑定导致 `errSecInternalComponent`

一个函数调用会产生双重borrow 错误,而另一个则不会

当你删除一个存在于堆栈中的值时,为什么 rust 不会抱怨

如何在 Rust 的泛型函​​数中同时使用非拥有迭代器和消费迭代器?

为什么 u64::trailing_zeros() 在无分支工作时生成分支程序集?

Rust:为什么在 struct 中borrow 引用会borrow 整个 struct?