有没有办法防止 Weak::new() 过度分配? [英] Is there a way to prevent Weak::new() from over allocating?

查看:42
本文介绍了有没有办法防止 Weak::new() 过度分配?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

跟进 为什么不Weak::new() 在 Rc::downgrade() 起作用时起作用吗?

当试图以不需要它为基础类型分配内存的方式实现 Weak::new() 时,即使它永远不会被使用,我遇到了障碍.

RcBox 的定义相当简单:

struct RcBox{强:细胞,弱:单元<使用大小>,值:T,}

这里的目标是创建一个 RcBox,它实际上不包含任何 value.本质上,一个 RcBox<()>.

但是,有一个障碍.*mut RcBox<()> 是一个瘦指针,但 *mut RcBox 可能是一个胖指针.我们有这个胖指针的数据部分是正确的,但是胖指针有很多不同的情况,因此尝试综合其余部分是困难.

正如在链接的问题中所见,我可以使它仅适用于 trait 对象:

impl弱 T{pub fn new() ->弱 T{不安全{让装箱 = Box::into_raw(box RcBox {强:单元格::新(0),弱:单元格::新(1),价值: (),});let ptr = if size_of::<*mut ()>() == size_of::<*mut T>() {let ptr: *mut RcBox= transmute_copy(&boxed);指针} 别的 {let ptr: *mut RcBox= transmute_copy(&TraitObject {数据:装箱为*mut(),虚表:null_mut(),});指针};弱 { ptr: Shared::new(ptr) }}}}

但是这不适用于 str(例如).

我再次尝试隔离 RcBox 的固定大小部分,同时让编译器推断指针的胖部分:

struct RcBox{计数器:RcBoxCounters<T>,值:T,}struct RcBoxCounters{强:细胞,弱:单元<使用大小>,_phantom: PhantomData,}impl<T: ?Sized>弱 T{pub fn new() ->弱 T{不安全{let boxed = Box::into_raw(box RcBox::Counters::new(0, 1));弱 { ptr: Shared::new(boxed as *mut RcBox) }}}}

这听起来很聪明,直到编译器压垮了你的热情:

<块引用>

error[E0375]:实现特征 `CoerceUnsized` 需要多次强制转换--><匿名>:58:40|58 |impl<T: ?Sized + Unsize <U >, U: ?Sized>CoerceUnsized<RcBox<U>用于 RcBox <T >{}|^^^^^^^^^^^^^^^^^^^^^^^ 需要多次强制|= 注意:`CoerceUnsized` 只能用于结构之间的强制转换,其中一个字段被强制转换= 注意:目前,有 2 个字段需要强制转换:计数器(RcBoxCounters 到 RcBoxCounters),值(T 到 U)

即:

  • 我认为为了让编译器合成胖部分,我需要在 RcBoxCounters 中添加一个 PhantomData
  • 但是,这样做需要 2 次强制转换,这是不允许的.
<小时>

那么,有没有办法修复 Weak::new() 以便它停止分配无关的(不必要的)内存?

注意:我的意思是只为两个计数器分配空间,然后分配大空间和修剪没有帮助.

注意:已经指出可以使用 Option 或特殊值来表示值的缺失.这需要对每个方法进行分支,这可能是不可取的.我更喜欢学习摆弄胖指针.

解决方案

是的,有办法,居然提交到标准库了:

<块引用>

这个改变使得 Weak::new() 根本不分配内存.相反,它是使用空指针创建的.使用 Wea​​k 所做的唯一事情就是尝试升级、克隆和删除,这意味着代码实际上需要检查指针是否为空的地方很少.

Follow-up of Why doesn't Weak::new() work when Rc::downgrade() does?

When attempting to implement Weak::new() in a way that would NOT require it to allocate memory for the underlying type even though it is never going to be used, I hit a roadblock.

The definition of RcBox<T> is rather simple:

struct RcBox<T: ?Sized> {
    strong: Cell<usize>,
    weak: Cell<usize>,
    value: T,
}

And the goal here is to create a RcBox<T> which will actually NOT contain any value. Essentially, a RcBox<()>.

However, there is a snag. *mut RcBox<()> is a thin pointer but *mut RcBox<T> is potentially a fat pointer. We have the data part of this fat pointer right, but there are many different cases of fat pointers so attempting to synthetize the rest is hard.

As can be seen in the linked question, I can make it work for just trait objects:

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) }
        }
    }
}

However this won't work with str (for example).

I made another attempt trying to isolate the fixed-size portion of RcBox while letting the compiler infer the fat part of the pointer:

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>) }
        }
    }
}

which sounds very clever until the compiler squashes your enthusiasm:

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)

That is:

  • I think that to let the compiler synthesize the fat part I need a PhantomData in RcBoxCounters,
  • however doing so requires 2 conversions for the coercion, which is not allowed.

So, is there a way to fix Weak::new() so that it stops allocating extraneous (unnecessary) memory?

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 Option 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.

解决方案

Yes, there is a way, and it was actually submitted to the standard library:

This change makes it so that Weak::new() allocates no memory at all. Instead, it is created with a null pointer. The only things done with a Weak are trying to upgrade, cloning, and dropping, meaning there are very few places that the code actually needs to check if the pointer is null.

这篇关于有没有办法防止 Weak::new() 过度分配?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆