2016-08-29 3 views
1

У меня есть следующий кодКак вы создаете ящик <T>, когда T - объект-признак?

extern crate rand; 
use rand::Rng; 

pub struct Randomizer { 
    rand: Box<Rng>, 
} 

impl Randomizer { 
    fn new() -> Self { 
     let mut r = Box::new(rand::thread_rng()); // works 
     let mut cr = Randomizer { rand: r }; 
     cr 
    } 

    fn with_rng(rng: &Rng) -> Self { 
     let mut r = Box::new(*rng); // doesn't work 
     let mut cr = Randomizer { rand: r }; 
     cr 
    } 
} 

fn main() {} 

Он жалуется, что

error[E0277]: the trait bound `rand::Rng: std::marker::Sized` is not satisfied 
    --> src/main.rs:16:21 
    | 
16 |   let mut r = Box::new(*rng); 
    |      ^^^^^^^^ `rand::Rng` does not have a constant size known at compile-time 
    | 
    = help: the trait `std::marker::Sized` is not implemented for `rand::Rng` 
    = note: required by `<std::boxed::Box<T>>::new` 

Я не понимаю, почему он требует Sized на Rng когда Box<T> не навязывает это на T.

+1

На самом деле это не имеет никакого отношения, с тем фактом, что вы позже храните это в члене, чтобы название было довольно неточным и не помогло дальнейшим искателям. Если у кого-то есть идея для лучшей идеи ... –

+0

Знаете ли вы о [объектах trait] (https://doc.rust-lang.org/book/trait-objects.html)? – kennytm

+0

@ MatthieuM. Вы правы, что титул нуждается в улучшении. – Andreas

ответ

4

Больше о Sized черта и оценка - это довольно особая черта, которая implicitly added для каждой функции, поэтому вы не видите его в списке в прототипе для Box::new:

fn new(x: T) -> Box<T> 

Обратите внимание, что он принимает значение x по значению (или перемещению), поэтому вам нужно знать, насколько велика возможность даже вызвать функцию.

Напротив, сам тип BoxнеSized; он использует (опять специальный) признак, связанный ?Sized, что означает «отказаться от дефолта Sized связанного»:

pub struct Box<T> where T: ?Sized(_); 

Если посмотреть, есть один способ создать Box с некалиброванным типом:

impl<T> Box<T> where T: ?Sized 
.... 
    unsafe fn from_raw(raw: *mut T) -> Box<T> 

поэтому из небезопасного кода вы можете создать его из необработанного указателя. С тех пор все нормальные вещи работают.

3

Проблема на самом деле довольно проста: у вас есть объект признака, и только две вещи, которые вы знаете об этом признака объекта являются:

  • свой список доступных методов
  • указатель на его данные

Когда вы запрашиваете, чтобы переместить этот объект в другое место памяти (здесь в куче), вам не хватает одного важной части информации: его размера.

Как вы узнаете, сколько памяти нужно зарезервировать? Сколько бит двигаться?

Когда объект Sized, эта информация известна во время компиляции, поэтому компилятор «вводит» ее для вас. Однако в случае объекта-объекта эта информация неизвестна (к сожалению), и поэтому это невозможно.

Было бы очень полезно предоставить эту информацию и иметь доступ к полиморфному перемещению/клону, но этого пока не существует, и я пока не помню никаких предложений по этому поводу, и я не знаю, быть (с точки зрения обслуживания, времени исполнения, ...).

1

Я также хочу, чтобы отправить ответ, что один способ справиться с этой ситуацией

fn with_rng<TRand: Rng>(rng: &TRand) -> Self { 
    let r = Box::new(*rng); 
    Randomizer { rand: r } 
} 

-мономорфизм Руста создаст необходимую реализацию with_rng замены TRand типа конкретного размера. Кроме того, вы можете добавить обязательную привязку, требующую, чтобы TRand был Sized.