Почему я не могу использовать вывод типа с объявлением массива?

Я объявил массив пользовательских черт Animal чтобы поэкспериментировать с полиморфизмом в Rust, но компилятор, кажется, вместо этого делает вывод типа для подтипа первого элемента:

fn main() {
    let animals = [Cat, Dog, Cat, Lion, Dog, Lion];
    for single_animal in animals {
        single_animal.talk();
    }
}

trait Animal {
    fn talk(&self);
}

struct Cat;
struct Dog;
struct Lion;

impl Animal for Cat {
    fn talk(&self) { 
        println!("Je miaule !");
    }
}

impl Animal for Dog {
    fn talk(&self) { 
        println!("J'aboie !");
    }
}

impl Animal for Lion {
    fn talk(&self) {
        println!("Je rugit !");
    }
}

Компилятор жалуется на тот факт, что первый элемент Cat а не остальные

error: mismatched types [--explain E0308]
 --> src/main.rs:3:25
  |>
3 |>     let animals = [Cat, Dog, Cat, Lion, Dog, Lion];
  |>                         ^^^ expected struct `Cat`, found struct `Dog`
note: expected type `Cat`
note:    found type `Dog`

error: mismatched types [--explain E0308]
 --> src/main.rs:3:35
  |>
3 |>     let animals = [Cat, Dog, Cat, Lion, Dog, Lion];
  |>                                   ^^^^ expected struct `Cat`, found struct `Lion`
note: expected type `Cat`
note:    found type `Lion`

error: mismatched types [--explain E0308]
 --> src/main.rs:3:41
  |>
3 |>     let animals = [Cat, Dog, Cat, Lion, Dog, Lion];
  |>                                         ^^^ expected struct `Cat`, found struct `Dog`
note: expected type `Cat`
note:    found type `Dog`

error: mismatched types [--explain E0308]
 --> src/main.rs:3:46
  |>
3 |>     let animals = [Cat, Dog, Cat, Lion, Dog, Lion];
  |>                                              ^^^^ expected struct `Cat`, found struct `Lion`
note: expected type `Cat`
note:    found type `Lion`

error: the trait bound `[Cat; 6]: std::iter::Iterator` is not satisfied [--explain E0277]
 --> src/main.rs:4:5
  |>
4 |>     for single_animal in animals {
  |>     ^
note: `[Cat; 6]` is not an iterator; maybe try calling `.iter()` or a similar method
note: required by `std::iter::IntoIterator::into_iter`

Добавление Animal тип к массиву также не решает проблему. Потому что на этот раз я получаю больше ошибок:

error: mismatched types [--explain E0308]
 --> src/main.rs:3:27
  |>
3 |>     let animals: Animal = [Cat, Dog, Cat, Lion, Dog, Lion];
  |>                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait Animal, found array of 6 elements
note: expected type `Animal`
note:    found type `[Cat; 6]`

error: the trait bound `Animal: std::marker::Sized` is not satisfied [--explain E0277]
 --> src/main.rs:3:9
  |>
3 |>     let animals: Animal = [Cat, Dog, Cat, Lion, Dog, Lion];
  |>         ^^^^^^^
note: `Animal` does not have a constant size known at compile-time
note: all local variables must have a statically known size

error: the trait bound `Animal: std::marker::Sized` is not satisfied [--explain E0277]
 --> src/main.rs:4:5
  |>
4 |>     for single_animal in animals {
  |>     ^
note: `Animal` does not have a constant size known at compile-time
note: required by `std::iter::IntoIterator::into_iter`

error: the trait bound `Animal: std::iter::Iterator` is not satisfied [--explain E0277]
 --> src/main.rs:4:5
  |>
4 |>     for single_animal in animals {
  |>     ^
note: `Animal` is not an iterator; maybe try calling `.iter()` or a similar method
note: required by `std::iter::IntoIterator::into_iter`

2 ответа

Решение

Массивы ржавчины являются однородными, что означает, что каждый элемент в нем имеет один и тот же тип. Таким образом, вы не можете иметь массив с Dogс и Cats. Но вы можете иметь массив, полный так называемых "объектов-черт", в вашем случае &Animal, Вот как мы явно запрашиваем полиморфизм во время выполнения.

Вы должны явно указать компилятору, что вам нужен массив, полный объектов признаков. Компилятор определяет тип массива на основе первого элемента в инициализаторе, поэтому давайте явно приведем эту вещь:

let animals = [&Cat as &Animal, &Dog, &Cat, &Lion, &Dog, &Lion];

Обратите внимание, что мы также добавили & ко всем значениям, потому что вы можете работать только с указателями для черт объектов. (впоследствии сообщается о другой маленькой ошибке в вашем коде, но решение довольно простое). Смотрите на детской площадке.

Это потому что Cat, Dog а также Lion все разные типы, и вы можете иметь только один в массиве.

Вы могли бы использовать объекты черт, как предложил Лукас, но то, что вам нужно, могло быть достигнуто гораздо проще (объекты черт - это не то, что я бы порекомендовал начинающему Rust), с общим Animal перечисление:

use self::Animal::*;

fn main() {
    let animals = [Cat, Dog, Cat, Lion, Dog, Lion];
    for single_animal in animals.iter() {
        single_animal.talk();
    }
}

trait AnimalSkills {
    fn talk(&self);
}

enum Animal {
    Cat,
    Dog,
    Lion
}

impl AnimalSkills for Animal {
    fn talk(&self) {
        match *self {
            Cat => println!("Je miaule !"),
            Dog => println!("J'aboie !"),
            Lion => println!("Je rugit !")
        }
    }
}

Также обратите внимание, что вам нужно позвонить .iter() для того, чтобы иметь возможность перебирать массив.

Другие вопросы по тегам