Object safety

W tym zadaniu zajmiemy się dwoma pokrewnymi zagadnieniami: dynamic dispatch oraz object safety.

Dany jest trait Musician oraz skończony zbiór typów implementujących ten trait:

trait Musician {
    fn play(&self);
}

mod my_band {
    pub struct Singer;
    impl super::Musician for Singer {
        fn play(&self) { println!(":studio_microphone:"); }
    }

    pub struct Guitarist;
    impl super::Musician for Guitarist {
        fn play(&self) { println!(":guitar:"); }
    }

    pub struct Pianist;
    impl super::Musician for Pianist {
        fn play(&self) { println!(":musical_keyboard:"); }
    }

    pub struct Drummer;
    impl super::Musician for Drummer {
        fn play(&self) { println!(":drum:"); }
    }
}

1. Zaimplementuj funkcję rehearse z odpowiednią sygnaturą, która przyjmuje dowolną kolekcję musicians (tzn. obiektów potencjalnie różnych typów, z których każdy implementuje Musician) i wywołuje na każdym elemencie play:

fn rehearse(musicians: ???) {
    for musician in musicians {
        musician.play();
    }
}

Przetestuj swoje rozwiązanie.

2. Dodaj nową metodę do traitu Musician:

trait Musician {
    fn play(&self);
    fn tune_to(&self, other: &Self) {
        println!("Tuning...");
    }
}

Spróbuj uruchomić program ponownie. Napraw go NIE ZMIENIAJĄC KODU TRAITU.

Losowość z odzysku

Czasem w testach potrzebujemy losowości. W części przypadków wystarcza nam deterministyczny generator liczb pseudolosowych, który zawsze zwraca te same wartości, natomiast w innych potrzebujemy prawdziwej losowości (tzn. żeby przy każdym odpaleniu testów sprawdzać inne wartości). W drugim scenariuszu bardzo przydatne jest, aby w razie błędu testy były w stanie powtórzyć losowe zachowanie, które doprowadziło do błędu. Innymi słowy, żebyśmy byli w stanie zreprodukować warunki, które doprowadziły testy do porażki.

Przygotuj nowy typ TestSeed:

pub struct TestSeed([u8; 32]);

Założenia:

  • można utworzyć tylko jeden obiekt TestSeed na wątek (thread),
  • domyślnie, TestSeed jest losowy (można korzystać z rand::thread_rng),
  • ustawiając zmienną środowiskową TEST_SEED na niepustą wartość, możemy wymusić ustawienie konkretnego ziarna (tzn. nadpisać domyślną losowość w konstruktorze),
  • w przypadku gdy wątek panikuje, upewniamy się, że TestSeed jest zapisywany do pliku recover_test_seed.

Przetestuj swoje rozwiązanie. Polecam skorzystać z rand::{rngs::StdRng, Rng, SeedableRng} oraz once_cell.