Вектор объектов, принадлежащих признаку
Рассмотрим следующий код:
trait Animal {
fn make_sound(&self) -> String;
}
struct Cat;
impl Animal for Cat {
fn make_sound(&self) -> String {
"meow".to_string()
}
}
struct Dog;
impl Animal for Dog {
fn make_sound(&self) -> String {
"woof".to_string()
}
}
fn main () {
let dog: Dog = Dog;
let cat: Cat = Cat;
let v: Vec<Animal> = Vec::new();
v.push(cat);
v.push(dog);
for animal in v.iter() {
println!("{}", animal.make_sound());
}
}
Компилятор говорит мне, что v
является вектором Animal
, когда я пытаюсь нажать cat
(тип несоответствия)
Итак, как я могу сделать вектор объектов, принадлежащих признаку, и вызывает соответствующий метод признаков для каждого элемента?
Ответы
Ответ 1
Vec<Animal>
не является законным, но компилятор не может вам сказать, потому что несоответствие типа каким-то образом скрывает его. Если мы удалим вызовы на push
, компилятор даст нам следующую ошибку:
<anon>:22:9: 22:40 error: instantiating a type parameter with an incompatible type `Animal`, which does not fulfill `Sized` [E0144]
<anon>:22 let mut v: Vec<Animal> = Vec::new();
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Причина, по которой это не является законным, заключается в том, что Vec<T>
хранит много объектов T
последовательно в памяти. Однако Animal
является признаком, а черты не имеют размера (a Cat
и a Dog
не имеют одинакового размера).
Чтобы решить эту проблему, нам нужно сохранить что-то, имеющее размер в Vec
. Наиболее простым решением является обернуть значения в Box
, т.е. Vec<Box<Animal>>
. Box<T>
имеет фиксированный размер ( "указатель жира", если T - это признак, простой указатель в противном случае).
Здесь работает main
:
fn main () {
let dog: Dog = Dog;
let cat: Cat = Cat;
let mut v: Vec<Box<Animal>> = Vec::new();
v.push(Box::new(cat));
v.push(Box::new(dog));
for animal in v.iter() {
println!("{}", animal.make_sound());
}
}
Ответ 2
Вы можете использовать объект ссылочного объекта &Animal
для заимствования элементов и сохранения этих объектов-признаков в Vec
. Затем вы можете перечислить его и использовать интерфейс признаков.
Изменение типового типа Vec
путем добавления &
перед признаком будет работать:
fn main() {
let dog: Dog = Dog;
let cat: Cat = Cat;
let mut v: Vec<&Animal> = Vec::new();
// ~~~~~~~
v.push(&dog);
v.push(&cat);
for animal in v.iter() {
println!("{}", animal.make_sound());
}
// Ownership is still bound to the original variable.
println!("{}", cat.make_sound());
}
Это здорово, если вы можете захотеть, чтобы исходная переменная продолжала владеть и повторно использовать ее позже.
Имейте ввиду, что вы не можете передать право собственности на dog
или cat
, потому что Vec
заимствовал эти конкретные экземпляры в той же области.
Знакомство с новой областью может помочь справиться с этой конкретной ситуацией:
fn main() {
let dog: Dog = Dog;
let cat: Cat = Cat;
{
let mut v: Vec<&Animal> = Vec::new();
v.push(&dog);
v.push(&cat);
for animal in v.iter() {
println!("{}", animal.make_sound());
}
}
let pete_dog: Dog = dog;
println!("{}", pete_dog.make_sound());
}