首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Rc<Trait>到Option<T>?

Rc<Trait>到Option<T>?
EN

Stack Overflow用户
提问于 2017-01-12 16:19:11
回答 2查看 693关注 0票数 2

我正在尝试实现如下方法:

代码语言:javascript
复制
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>中的函数。我能找到的最接近的事情是

代码语言:javascript
复制
if Rc::strong_count(&rc) == 1 {
    Some(unsafe {
        Box::from_raw(Rc::into_raw(rc))
    })
} else {
    None
}

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

有什么办法来实现这个吗?

操场连接,我在这里寻找一个rc_to_box的实现。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-01-12 19:07:10

不幸的是,Rc的API似乎缺乏必要的方法来获得包装类型的所有权,当它是!Sized时。

唯一可以返回Rc内部项的方法是Rc::try_unwrap,但是它返回Result<T, Rc<T>>,它要求TSized

为了做您想做的事情,您需要有一个带有签名的方法:Rc<T> -> Result<Box<T>, Rc<T>>,这将允许T成为!Sized,从那里您可以提取Box<Any>并执行downcast调用。

但是,由于Rc是如何实现的,这种方法是不可能的。下面是Rc的简化版本

代码语言:javascript
复制
struct RcBox<T: ?Sized> {
    strong: Cell<usize>,
    weak: Cell<usize>,
    value: T,
}

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

因此,您可以从Rc<T>中获得的唯一的Rc<T>Box<RcBox<T>>

请注意,这里的设计受到严重限制:

  • 单一分配要求所有三个元素都在一个struct中。
  • T: ?Sized规定T是最后一个字段

因此,总体上几乎没有改进的余地。

但是,在您的具体情况下,绝对有可能改进泛型情况。当然,它需要unsafe代码。虽然它在Rc中运行得很好,但是使用Arc实现它将因潜在的数据竞争而变得复杂。

哦..。而守则是按原样提供的,不隐含任何保证;)

代码语言:javascript
复制
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));
}
票数 3
EN

Stack Overflow用户

发布于 2017-01-12 17:34:23

如果您期望concretify函数将原始值移回Rc之外,我认为实现它是不可能的;请参阅这个问题了解原因。

如果您愿意返回一个克隆人,这很简单:

代码语言:javascript
复制
fn concretify<T: Any+Clone>(rc: Rc<Any>) -> Option<T> {
    rc.downcast_ref().map(Clone::clone)
}

下面是一个测试:

代码语言:javascript
复制
#[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)) 回来:一个也没有

游乐场

如果你想要更“动态”的东西,并且创建你的价值是便宜的,你也可以做一个虚拟的,使用downcast_mut()而不是downcast_ref(),然后std::mem::swap与虚拟。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41618100

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档