当Rc :: downgrade()这样做时,为什么没有Weak :: new()工作?

时间:2017-06-15 17:33:40

标签: rust

我正在创建一个函数,该函数返回对特征对象的Weak引用。在无法找到对象的情况下(它是查找函数),我想使用Weak返回空的Weak::new()引用:

use std::rc::{self, Rc, Weak};
use std::cell::RefCell;

pub trait Part {}

pub struct Blah {}

impl Part for Blah {}

fn main() {
    let blah = Blah {};
    lookup(Rc::new(RefCell::new(blah)));
}

fn lookup(part: Rc<RefCell<Part>>) -> Weak<RefCell<Part>> {
    if true {
        Rc::downgrade(&part)
    } else {
        Weak::new()
    }
}

编译期间出现以下错误:

error[E0277]: the trait bound `Part + 'static: std::marker::Sized` is not satisfied in `std::cell::RefCell<Part + 'static>`
  --> <anon>:19:9
   |
19 |         Weak::new()
   |         ^^^^^^^^^ within `std::cell::RefCell<Part + 'static>`, the trait `std::marker::Sized` is not implemented for `Part + 'static`
   |
   = note: `Part + 'static` does not have a constant size known at compile-time
   = note: required because it appears within the type `std::cell::RefCell<Part + 'static>`
   = note: required by `<std::rc::Weak<T>>::new`

为什么我可以从Weak<RefCell<Part>>成功创建Rc::downgrade()但不能使用相同的类型来创建Weak::new()的新弱引用?

我有没有办法注释Weak::new()来帮助编译器,还是我必须将它包装在Option中以让用户知道找不到该部分?

Working minimal example

2 个答案:

答案 0 :(得分:4)

Weak::new()推断的类型为Weak<RefCell<Part>>,无法创建Part部分,因为它是一个特征!

Sized错误的全部内容。特性不是一个具体的结构,它在编译时没有已知的大小,因此编译器不知道要分配多少空间。

  

为什么我可以从Weak<RefCell<Part>>

成功创建Rc::downgrade()

这是因为Rc<RefCell<Part>>指向已经分配的结构。编译器可以使用特征指针引用它,即使它不知道它是Blah还是Part特征的其他实现。

  

我有没有办法注释Weak::new()来帮助编译器

您确实可以注释Weak::new(),将编译器指向您想要实例化的Part实现,如下所示:

use std::rc::{Rc, Weak};
use std::cell::RefCell;

pub trait Part {}

pub struct Blah {}

impl Part for Blah {}

fn main() {
    let blah = Blah {};
    lookup(Rc::new(RefCell::new(blah)));
}

fn lookup(part: Rc<RefCell<Part>>) -> Weak<RefCell<Part>> {
    if true {
        Rc::downgrade(&part)
    } else {
        Weak::<RefCell<Blah>>::new()
    }
}

答案 1 :(得分:2)

TL; DR:胖指针很难。

因此,您需要在强制实施之前明确指定具体类型:

Weak::<RefCell<Blah>>::new()

注意:如果Blah占用大量内存,请创建零大小类型Fool,为其实现Part(所有函数unimplemented!()),然后使用Weak::<RefCell<Fool>>::new()来避免无用地分配内存。

我认为潜在的问题只是实施问题之一。

它似乎不可修复,但可能需要相当多的工作来涵盖所有极端情况。

首先,让我们揭露问题。

Weak::new的实施:

impl<T> Weak<T> {
    pub fn new() -> Weak<T> {
        unsafe {
            Weak {
                ptr: Shared::new(Box::into_raw(box RcBox {
                    strong: Cell::new(0),
                    weak: Cell::new(1),
                    value: uninitialized(),
                })),
            }
        }
    }
}

对于同质性,所有Shared元素都包含RcBox,其中包含两个Cell(计数器)和实际值。

构建RcBox<T> 这一事实需要知道T的大小,这就是为什么与大多数Weak方法不同,{{1}在此T中未标记为: ?Sized

现在,由于内存未被初始化,很明显它永远不会被使用,所以实际任何大小都没问题。

impl实际上可以携带未经过身份验证的数据,这是RcBoxRcBox<Struct>所必需的,因此RcBox<Trait>和{{ 1}}字段总是先布置 (只有最后一个字段可以不用)。

因此,我们希望

  1. 分配strong,这样可以节省内存并且不需要weakRcBox<()>
  2. 然后转换为T,无论Sized是什么。
  3. 好的,让我们做吧!

    我们期望的实现将如下所示:

    RcBox<T>

    完全无法编译。

    为什么呢?因为T是精简指针,而impl<T: ?Sized> Weak<T> { pub fn new() -> Weak<T> { unsafe { Weak { ptr: Shared::new(transmute(Box::into_raw(box RcBox { strong: Cell::new(0), weak: Cell::new(1), value: (), }))), } } } } 是精简指针或胖指针(see raw memory representation),取决于*mut RcBox<()>*mut RcBox<T>还是{{} 1}}。

    现在,特征指针can be handled警告:包含T 的简化且完全不安全的实现),以及Sized的以下实现:

    !Sized

    然而,这个实现只考虑特征指针,还有其他类型的胖指针,它可能会完全崩溃。