Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I choose a random value from an enum?

Tags:

The spinners crate has an enum with a large selection of possible spinners.

Here's the enum (with all values except the top and bottom 4 skipped):

pub enum Spinners {     Dots,     Dots2,     Dots3,     Dots4,     ...     Shark,     Dqpb,     Weather,     Christmas, } 

A new spinner is easy to create:

extern crate spinners;  use spinners::{Spinner, Spinners}; use std::thread::sleep; use std::time::Duration;  fn main() {     let sp = Spinner::new(Spinners::Dots9, "Waiting for 3 seconds".into());     sleep(Duration::from_secs(3));     sp.stop(); } 

However, I wish to select a spinner at random, and this does not work:

let spinner_enum = rng.choose(Spinners).unwrap_or(&Spinners::Dots9); 

Because:

error[E0423]: expected value, found enum `Spinners`  let spinner_enum = rng.choose(Spinners).unwrap_or(&Spinners::Dots9);                               ^^^^^^^^ not a value 

How can I choose an enum value at random, and use that to display a random spinner?

like image 334
Alexander Avatar asked Jan 28 '18 18:01

Alexander


People also ask

Can we use @value in enum?

No, it's not. The purpose for an Enum is to give us mapped data (fixed set of values) with limited scope. If we would to use @Value in Java enums, it would take the purpose of the enum away in the first place.

How to get a random value from an enum in Java?

To allow us to get random value of this BaseColor enum we define a getRandomColor () method in the enum. This method use the java.util.Random to create a random value. This random value then will be used to pick a random value from the enum. Let’s see the code snippet below:

How to get random value of basecolor in Java?

First we’ll create an enum called BaseColor which will have three valid value. These values are Red, Green and Blue. To allow us to get random value of this BaseColor enum we define a getRandomColor () method in the enum. This method use the java.util.Random to create a random value.

Can you set the default values of an enum?

If you don't explicity set the values of the enums, the first element in the sequence is 0 and +1 for each going down. You can do (assuming the variable cardSuit is of type CardSuit (the enum): One more example that once again only applies to enums that haven't had their default values set.

What is the difference between randomenum() and randomdirection() in Java?

Notice how the randomEnum () method resembles the randomDirection () method from the previous example. The difference is that the RandomEnumGenerator class has a constructor that expects an enum type from which to get the constant values. We could generate a random direction using the RandomEnumGenerator class as follows:


1 Answers

Your own enum

Like most abstractions in Rust, random value generation is powered by traits. Implementing a trait is the same for any particular type, the only difference is exactly what the methods and types of the trait are.

Rand 0.5, 0.6, 0.7, and 0.8

Implement Distribution using your enum as the type parameter. You also need to choose a specific type of distribution; Standard is a good default choice. Then use any of the methods to generate a value, such as rand::random:

use rand::{     distributions::{Distribution, Standard},     Rng, }; // 0.8.0  #[derive(Debug)] enum Spinner {     One,     Two,     Three, }  impl Distribution<Spinner> for Standard {     fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Spinner {         // match rng.gen_range(0, 3) { // rand 0.5, 0.6, 0.7         match rng.gen_range(0..=2) { // rand 0.8             0 => Spinner::One,             1 => Spinner::Two,             _ => Spinner::Three,         }     } }  fn main() {     let spinner: Spinner = rand::random();     println!("{:?}", spinner); } 

Rand 0.4

Implement Rand for your enum, then use any of the methods to generate a value, such as Rng::gen:

extern crate rand; // 0.4.2  use rand::{Rand, Rng};  #[derive(Debug)] enum Spinner {     One,     Two,     Three, }  impl Rand for Spinner {     fn rand<R: Rng>(rng: &mut R) -> Self {         match rng.gen_range(0, 3) {             0 => Spinner::One,             1 => Spinner::Two,             _ => Spinner::Three,         }     } }  fn main() {     let mut rng = rand::thread_rng();     let spinner: Spinner = rng.gen();     println!("{:?}", spinner); } 

Derive

The rand_derive crate can remove the need for some of this boilerplate, but does not exist for Rand 0.5.

extern crate rand; #[macro_use] extern crate rand_derive;  use rand::Rng;  #[derive(Debug, Rand)] enum Spinner {     One,     Two,     Three, }      fn main() {     let mut rng = rand::thread_rng();     let spinner: Spinner = rng.gen();     println!("{:?}", spinner); } 

Someone else's enum

Since you don't control the enum, you have to copy something into your code in order to reference it. You could create an array of the enum and choose from that:

use rand::seq::SliceRandom; // 0.8.0  mod another_crate {     #[derive(Debug)]     pub enum Spinner {         One,         Two,         Three,     } }  fn main() {     let mut rng = rand::thread_rng();     let spinners = [         another_crate::Spinner::One,         another_crate::Spinner::Two,         another_crate::Spinner::Three,     ];     let spinner = spinners.choose(&mut rng).unwrap();     println!("{:?}", spinner); } 

You could replicate the entire enum locally, implement Rand for that, and then have a method that converts back into the other crates representation.

use rand::{     distributions::{Distribution, Standard},     Rng, }; // 0.8.0  mod another_crate {     #[derive(Debug)]     pub enum Spinner {         One,         Two,         Three,     } }  enum Spinner {     One,     Two,     Three, }  impl From<Spinner> for another_crate::Spinner {     fn from(other: Spinner) -> another_crate::Spinner {         match other {             Spinner::One => another_crate::Spinner::One,             Spinner::Two => another_crate::Spinner::Two,             Spinner::Three => another_crate::Spinner::Three,         }     } }  impl Distribution<Spinner> for Standard {     fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Spinner {         match rng.gen_range(0..=2) {             0 => Spinner::One,             1 => Spinner::Two,             _ => Spinner::Three,         }     } }  fn main() {     let spinner = another_crate::Spinner::from(rand::random::<Spinner>());     println!("{:?}", spinner); } 

You could count the number of spinners and do a match:

use rand::Rng; // 0.8.0  mod another_crate {     #[derive(Debug)]     pub enum Spinner {         One,         Two,         Three,     } }  fn rando<R: Rng>(mut rng: R) -> another_crate::Spinner {     match rng.gen_range(0..=2) {         0 => another_crate::Spinner::One,         1 => another_crate::Spinner::Two,         _ => another_crate::Spinner::Three,     } }  fn main() {     let mut rng = rand::thread_rng();     let spinner = rando(&mut rng);     println!("{:?}", spinner); } 

You can implement a newtype and implement the random generation for that:

use rand::{distributions::Standard, prelude::*}; // 0.8.0  mod another_crate {     #[derive(Debug)]     pub enum Spinner {         One,         Two,         Three,     } }  struct RandoSpinner(another_crate::Spinner);  impl Distribution<RandoSpinner> for Standard {     fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> RandoSpinner {         RandoSpinner(match rng.gen_range(0..=2) {             0 => another_crate::Spinner::One,             1 => another_crate::Spinner::Two,             _ => another_crate::Spinner::Three,         })     } }  fn main() {     let RandoSpinner(spinner) = rand::random();     println!("{:?}", spinner); } 

See also:

  • How do I implement a trait I don't own for a type I don't own?
like image 131
Shepmaster Avatar answered Oct 06 '22 21:10

Shepmaster