2015-07-05 3 views
5

için genel işaretçi türü Ben bir değer başvuran bir struct var (çünkü ?Sized veya çok büyük). Bu değer elbette yapı ile yaşamak zorunda.
Ancak, yapının bunu gerçekleştirmesi için kullanıcısını ile kısıtlamaması gerekir. Kullanıcının değeri Box ya da Rc ya da 'static yapıp yapmadığı, değerin yapıyla birlikte hayatta kalması gerekir. Adlandırılmış yaşam sürelerini kullanmak karmaşık olabilir çünkü referans çevreye taşınır ve bizim struct'umuzu geçebilir. Aradığım şey genel bir işaretçi tipidir (eğer varsa/mevcutsa).`Rc`,` Box`, `Arc`

Yapı, başvurulan değerin, nasıl yapıldığını belirtmeksizin, yapının yaşandığı sürece yaşadığından nasıl emin olabilir?

Örnek (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); 

derler, tüm iyi. Ancak Box fonksiyonumuza bir işaretçi olarak kullanmak istemezsek (Box bir işaretçiyi, sağa mı?), Ama Rc gibi başka bir şey,işaretçiyi 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)

Olası çözüm: 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); 
} 

bu Rust ile gitmek yolu var mı? Deref kullanıyor musunuz? En azından çalışır.

Belirgin bir şey mi eksik? Değer, T olarak pratik olarak kullanılamaz olduğu için, bu sorunu çözmedim

cevap

3

Deref gerekli işlevleri sağlar iken, AsRef ve Borrow bu duruma (Borrow daha çok fazla AsRef bir yapı söz konusu olduğunda) için daha uygundur.Bu özelliklerin her ikisi de kullanıcılarınızın Box<T>, Rc<T> ve Arc<T> ve Borrow kullanmasına izin verir, ayrıca &T ve T'u kullanmasına izin verin. Sizin Caller yapı böyle yazılmış olabilir:

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

Geçerli sabit derleyici (1.1) ile çöker, ancak beta veya gece ile değil (yalnızca son Yürütme bağlantınızı kullanın ve üst kısımdaki "Kanal" ayarını değiştirin). Rc<Trait> için desteğin 1.1'de yalnızca kısmi olduğunu düşünüyorum; zamanında yapmayan bazı değişiklikler vardı. Bu muhtemelen kodunun neden kodunuzun çalışmaz.

Bunun için Deref'u kullanma sorusunu yanıtlamak için ... işaretçisini kaldırmanız yeterlidir ... tabi. Bu sadece, seçtiğiniz özelliklerin ihtiyaç duyduğunuz işlemleri destekleyip desteklemediğine dair bir soru. Varsa, harika.

Her zaman, ihtiyaç duyduğunuz tam semantiğini ifade eden yeni bir özellik yazıp varolan türler için bunu uygulayabilirsiniz. Söylediklerinizden, bu durumda gerekli görünmüyor.

+0

Birlikte bir düzenleme yaptı: Eğer callback alanını kullanmak istediğinizde

use std::borrow::Borrow; struct Caller<CB: Borrow<Callback>> { callback: CB, } 

Ardından, borrow() (veya as_ref()) yöntemini çağırmanız gerekir Bunun için bir çözüm, sadece bir kutuda değeri tekrar sardım. Yine de, bunun için Deref' * * çözüm kullanıyor? – Kapichu

+0

Cevabı genişletdim. Bunu ilk etapta ele almalıydım (sanırım acil problemi çözerek dikkatimi dağıttım) –