`Rc`,`Box`,`Arc`的常规指针类型

时间:2015-07-05 11:24:25

标签: rust

我有struct引用一个值(因为它是?Sized或非常大)。当然,这个值必须与结构一起使用 但是,结构不应该限制用户如何来实现它。无论用户是将值包装在Box还是Rc中还是使其成为'static,该值都必须与结构一起存活。使用命名生命周期会很复杂,因为引用将被移动并且可能比我们的struct更长。我正在寻找的是一般指针类型(如果它存在/可以存在)。

如果结构存在,结构如何确保引用的值存在,而不指定如何?

示例(is.gd/Is9Av6):

type CallBack = Fn(f32) -> f32;

struct Caller {
    call_back: Box<CallBack>,
}

impl Caller {
    fn new(call_back: Box<CallBack>) -> Caller {
        Caller {call_back: call_back}
    }

    fn call(&self, x: f32) -> f32 {
        (self.call_back)(x)
    }
}

let caller = {
    // func goes out of scope
    let func = |x| 2.0 * x; 
    Caller {call_back: Box::new(func)}
};

// func survives because it is referenced through a `Box` in `caller`
let y = caller.call(1.0);
assert_eq!(y, 2.0);

编译,一切都很好。但是如果我们不想使用Box作为指向我们函数的指针(可以调用Box一个指针,对吧?),还有别的东西,比如Rc,这不会是可能的,因为Caller将指针限制为Box

let caller = {
    // function is used by `Caller` and `main()` => shared resource
    // solution: `Rc`
    let func = Rc::new(|x| 2.0 * x); 
    let caller = Caller {call_back: func.clone()}; // ERROR Rc != Box

    // we also want to use func now
    let y = func(3.0);

    caller
};

// func survives because it is referenced through a `Box` in `caller`
let y = caller.call(1.0);
assert_eq!(y, 2.0);

is.gd/qUkAvZ

可能的解决方案:Deref? (http://is.gd/mmY6QC

use std::rc::Rc;
use std::ops::Deref;

type CallBack = Fn(f32) -> f32;

struct Caller<T>
        where T: Deref<Target = Box<CallBack>> {
    call_back: T,
}

impl<T> Caller<T> 
        where T: Deref<Target = Box<CallBack>> {
    fn new(call_back: T) -> Caller<T> {
        Caller {call_back: call_back}
    }

    fn call(&self, x: f32) -> f32 {
        (*self.call_back)(x)
    }
}

fn main() {
    let caller = {
        // function is used by `Caller` and `main()` => shared resource
        // solution: `Rc`
        let func_obj = Box::new(|x: f32| 2.0 * x) as Box<CallBack>;
        let func = Rc::new(func_obj); 
        let caller = Caller::new(func.clone());

        // we also want to use func now
        let y = func(3.0);

        caller
    };

    // func survives because it is referenced through a `Box` in `caller`
    let y = caller.call(1.0);
    assert_eq!(y, 2.0);
}

这是Rust的方式吗?使用Deref?它起码至少。

我错过了一些明显的东西吗?

This question无法解决我的问题,因为该值实际上无法用作T

2 个答案:

答案 0 :(得分:3)

虽然Deref提供了必要的功能,AsRefBorrow更适合这种情况(BorrowAsRef更适合结构)。这两个特征都可以让您的用户使用Box<T>Rc<T>Arc<T>,而Borrow也可以让他们使用&TT。您的Caller结构可以这样写:

use std::borrow::Borrow;

struct Caller<CB: Borrow<Callback>> {
    callback: CB,
}

然后,当您想要使用callback字段时,需要调用borrow()(或as_ref())方法:

impl<CB> Caller<CB> 
    where CB: Borrow<Callback>
{
    fn new(callback: CB) -> Caller<CB> {
        Caller { callback: callback }
    }

    fn call(&self, x: f32) -> f32 {
        (self.callback.borrow())(x)
    }
}

答案 1 :(得分:1)

它与当前稳定的编译器(1.1)崩溃,但不与beta或nightly崩溃(只需使用您的上一个Playypen链接并更改&#34; Channel&#34;设置在顶部)。我认为{1.1}中对Rc<Trait>的支持仅为 partial ;有一些变化并没有及时实现。这可能是为什么您的代码无法正常工作。

要解决使用Deref的问题......如果解除引用指针就是你需要的......当然。这真的只是一个问题,即您选择的特征是否支持您需要的操作。如果是的话,很棒。

另外,您总是可以编写一个表达所需 exact 语义的新特征,并为现有类型实现该特征。从你所说的话来看,在这种情况下似乎没有必要。