Rc <trait>到Option <t>?

时间:2017-01-12 16:19:12

标签: rust trait-objects

我正在尝试实现一个看起来像这样的方法:

fn concretify<T: Any>(rc: Rc<Any>) -> Option<T> {
    Rc::try_unwrap(rc).ok().and_then(|trait_object| {
        let b: Box<Any> = unimplemented!();
        b.downcast().ok().map(|b| *b)
    })
}

但是,try_unwrap不适用于特征对象(这是有意义的,因为它们未被大小化)。我的下一个想法是试图找到一些函数直接将Rc<Any>展开到Box<Any>。我能找到的最接近的是

if Rc::strong_count(&rc) == 1 {
    Some(unsafe {
        Box::from_raw(Rc::into_raw(rc))
    })
} else {
    None
}

但是,Rc::into_raw()似乎要求Rc中包含的类型为Sized,理想情况下我不想使用unsafe块。

有没有办法实现这个?

Playground Link,我在这里寻找rc_to_box的实现。

2 个答案:

答案 0 :(得分:3)

不幸的是,似乎Rc的API缺少必要的方法,可以在!Sized时获得包装类型的所有权。

唯一可以返回Rc内部项目的方法是Rc::try_unwrap,但会返回Result<T, Rc<T>>,这要求TSized。< / p>

为了做你想做的事,你需要一个带有签名的方法:Rc<T> -> Result<Box<T>, Rc<T>>,这将允许T成为!Sized,然后你就可以从中提取Box<Any>并执行downcast来电。

但是,由于Rc的实现方式,这种方法是不可能的。以下是Rc的精简版:

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

pub struct Rc<T: ?Sized> {
    ptr: *mut RcBox<T>,
    _marker: PhantomData<T>,
}

因此,Box中唯一Rc<T> Box<RcBox<T>>struct

请注意,此处的设计受到严格限制:

  • 单一分配要求所有3个元素都在一个T: ?Sized
  • T强制要求unsafe成为最后一个字段

所以一般来说几乎没有改进的余地。

但是,在您的具体情况下,绝对可以改善一般情况。当然,它确实需要Rc代码。虽然它与Arc的效果相当不错,但使用use std::any::Any; use std::{cell, mem, ptr}; use std::rc::Rc; struct RcBox<T: ?Sized> { strong: cell::Cell<usize>, _weak: cell::Cell<usize>, value: T, } fn concretify<T: Any>(rc: Rc<Any>) -> Option<T> { // Will be responsible for freeing the memory if there is no other weak // pointer by the end of this function. let _guard = Rc::downgrade(&rc); unsafe { let killer: &RcBox<Any> = { let killer: *const RcBox<Any> = mem::transmute(rc); &*killer }; if killer.strong.get() != 1 { return None; } // Do not forget to decrement the count if we do take ownership, // as otherwise memory will not get released. let result = killer.value.downcast_ref().map(|r| { killer.strong.set(0); ptr::read(r as *const T) }); // Do not forget to destroy the content of the box if we did not // take ownership if result.is_none() { let _: Rc<Any> = mem::transmute(killer as *const RcBox<Any>); } result } } fn main() { let x: Rc<Any> = Rc::new(1); println!("{:?}", concretify::<i32>(x)); } 实现它会因潜在的数据竞争而变得复杂。

哦......代码按原样提供,不提供任何保证;)

from PIL import Image
import operator, itertools

def get_alpha_channel(image): 
   try: 
      alpha_index = image.getbands().index('A')
   except ValueError:
      # no alpha channel, so convert to RGBA
      image = image.convert('RGBA')
      alpha_index = image.getbands().index('A')
   alpha_getter = operator.itemgetter(alpha_index)
   return itertools.imap(alpha_getter, image.getdata())

答案 1 :(得分:2)

如果您希望将原始值移回concretify,我认为您无法实施Rc功能。请参阅this question了解原因。

如果你愿意回复克隆,那就很简单:

fn concretify<T: Any+Clone>(rc: Rc<Any>) -> Option<T> {
    rc.downcast_ref().map(Clone::clone)
}

这是一个测试:

#[derive(Debug,Clone)]
struct Foo(u32);

#[derive(Debug,Clone)]
struct Bar(i32);

fn main() {
    let rc_foo: Rc<Any> = Rc::new(Foo(42));
    let rc_bar: Rc<Any> = Rc::new(Bar(7));

    let foo: Option<Foo> = concretify(rc_foo);
    println!("Got back: {:?}", foo);
    let bar: Option<Foo> = concretify(rc_bar);
    println!("Got back: {:?}", bar);
}

输出:

  

回头:一些(Foo(42))

     

回头:没有

Playground

如果你想要更多的东西&#34; movey&#34;,并且创造你的价值便宜,你也可以制作假人,使用downcast_mut()而不是downcast_ref(),然后{{1假人。