Questo è un comportamento corretto, anche se è un po 'sfortunato.
Nel primo caso abbiamo questa:
noti che push()
, quando ha invitato Vec<Box<Quack>>
, accetta Box<Quack>
, e si sta passando Box<Duck>
. Questo è OK - rustc è in grado di capire che si desidera convertire un valore in scatola a un oggetto tratto, come qui:
let duck: Box<Duck> = Box::new(Duck);
let quack: Box<Quack> = duck; // automatic coercion to a trait object
Nel secondo caso abbiamo questo:
let mut lake: Vec<Rc<RefCell<Box<Quack>>>> = Vec::new();
let mallard: Rc<RefCell<Box<Duck>>> = Rc::new(RefCell::new(Box::new(Duck)));
lake.push(mallard);
Qui push()
accetta Rc<RefCell<Box<Quack>>>
mentre si fornisce Rc<RefCell<Box<Duck>>>
:
let mallard: Rc<RefCell<Box<Duck>>> = Rc::new(RefCell::new(Box::new(Duck)));
let quack: Rc<RefCell<Box<Quack>>> = mallard;
e ora c'è un problema. Box<T>
è un tipo compatibile con DST, quindi può essere utilizzato come contenitore per un oggetto tratto. La stessa cosa sarà presto vera per Rc
e altri puntatori intelligenti quando è implementato lo this RFC. Tuttavia, in questo caso non vi è alcuna coercizione da un tipo concreto a un oggetto tratto perché Box<Duck>
si trova all'interno di livelli aggiuntivi di tipi (Rc<RefCell<..>>
).
Ricordare, l'oggetto tratto è un puntatore grasso, quindi Box<Duck>
è diverso dalla dimensione Box<Quack>
. Di conseguenza, in linea di principio, non sono direttamente compatibili: non puoi semplicemente prendere i byte di Box<Duck>
e scriverli dove è atteso Box<Quack>
. Rust esegue una conversione speciale, ovvero ottiene un puntatore alla tabella virtuale per Duck
, costruisce un puntatore grassetto e lo scrive nella variabile di tipo Box<Quack>
.
Quando si dispone di Rc<RefCell<Box<Duck>>>
, tuttavia, rustc avrebbe bisogno di sapere come costruire e destrutturare sia RefCell
e Rc
al fine di applicare la stessa conversione puntatore grasso per i suoi interni. Naturalmente, poiché questi sono tipi di libreria, non può sapere come farlo. Questo vale anche per qualsiasi altro tipo di wrapper, ad es. Arc
o Mutex
o anche Vec
. Non ti aspetti che sarebbe possibile utilizzare Vec<Box<Duck>>
come Vec<Box<Quack>>
, giusto?
Inoltre, nell'esempio con Rc
gli Rcs creati da Box<Duck>
e Box<Quack>
non sarebbero stati collegati: avrebbero avuto contatori di riferimento diversi.
Ovvero, una conversione da un tipo concreto a un oggetto tratto può avvenire solo se si ha accesso diretto a un puntatore intelligente che supporta l'ora legale, non quando è nascosto all'interno di un'altra struttura.
Detto questo, vedo come sia possibile impostare per alcuni tipi di selezione. Ad esempio, potremmo introdurre alcuni tipi di tratti di Construct
/Unwrap
che sono noti al compilatore e che potrebbe utilizzare per "raggiungere" all'interno di una pila di wrapper ed eseguire la conversione dell'oggetto trait al loro interno. Tuttavia, nessuno ha progettato questa cosa e ne ha fornito ancora una RFC - probabilmente perché non è una caratteristica ampiamente necessaria.
Nota: 'RefCell' non è necessario qui, posso riprodurre il problema con' Rc> '. –
Matthieu, hai ragione. 'RefCell' non è necessario per far si che l'errore si verifichi. Sulla base della risposta di Vladimir in basso, posso capire perché lo stesso errore si verifica con o senza 'RefCell'. – rlkw1024