Вектор объектов, принадлежащих признаку
Рассмотрим следующий код:
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 = Vec::new(); v.push(cat); v.push(dog); for animal in v.iter() { println!("{}", animal.make_sound()); } }
Компилятор говорит мне, что v
является вектором Animal
когда я пытаюсь нажать cat
(несоответствие типа)
Итак, как я могу сделать вектор объектов, принадлежащих к признаку, и называет соответствующий метод признаков для каждого элемента?
- Почему не рекомендуется принимать ссылку на String (& String), Vec (& Vec) или Box (& Box) в качестве аргумента функции?
- Можно ли управлять размером массива с помощью параметра типа общего?
- Как синтаксис отличается от регулярной привязки к жизни?
- Почему этот шаблон совпадения недоступен при использовании нелитеральных шаблонов?
- Есть ли способ создать псевдоним типа для нескольких признаков?
- Пакет ржавчины с библиотекой и двоичным?
- Есть ли способ объединить несколько признаков, чтобы определить новый признак?
- Как избежать дублирования функций доступа для изменяемых и неизменных ссылок в Rust?
- Автоматически реализовать черты замкнутого типа для новых типов Rust (кортежи с одним полем)
- Зачем пытаться! () И? не компилироваться при использовании в функции, которая не возвращает результат?
- Почему черта не реализована для типа, который ее явно реализовал?
- Каков правильный способ вернуть Итератор (или любой другой признак)?
- Как проверить равенство между объектами признаков?
Vec
не является законным, но компилятор не может вам сказать, потому что несоответствие типа каким-то образом скрывает его. Если мы удалим вызовы для push
, компилятор даст нам следующую ошибку:
:22:9: 22:40 error: instantiating a type parameter with an incompatible type `Animal`, which does not fulfill `Sized` [E0144] :22 let mut v: Vec = Vec::new(); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Причина, почему это не является законным, заключается в том, что Vec
хранит много объектов T
последовательно в памяти. Тем не менее, Animal
– это черта, и черты не имеют размера (у Cat
и Dog
не гарантируется одинаковый размер).
Чтобы решить эту проблему, нам нужно сохранить что-то, что имеет размер в Vec
. Наиболее простым решением является обертывание значений в Box
, то есть Vec
. Box
имеет фиксированный размер («указатель жира», если T – это признак, простой указатель в противном случае).
Вот main
:
fn main () { let dog: Dog = Dog; let cat: Cat = Cat; let mut v: Vec> = Vec::new(); v.push(Box::new(cat)); v.push(Box::new(dog)); for animal in v.iter() { println!("{}", animal.make_sound()); } }
Вы можете использовать объект ссылочного объекта &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()); }