Algorithmic Canary
Algorithmic Canary

Reputation: 742

Idiomatic way to create static iterable collection of named structs?

What is the idiomatic way to create static iterable collection of named structs? I have n instances of a struct, where n is known at compile time and is less than 20. I would like to be able to iterate over all the entries and also be able to refer to each entry by a name instead of an index. All the data is known at compile time.

I could use an array or enum, along with hand written constants which map the labels to indexes; but this seems finicky.

fn common_behaviour(x: f64) {
   print!("{}", x);
}

const ADD: usize = 0;
const SUBTRACT: usize = 1;

fn main () {
    let mut foos: [f64; 2] = [0.0; 2];
    foos[ADD] = 4.0;
    foos[SUBTRACT] = 2.0;

    for foo in &foos {
       common_behaviour(*foo);
    }
    foos[ADD] += 1.0;
    foos[SUBTRACT] -= 1.0;
}

Alternatively, I could just pay the performance cost and use a HashMap as the hashing overhead might not actually matter that much, but this seems suboptimal as well.

Perhaps, I could refactor my code to use function pointers instead special casing the different special cases.

fn common_behaviour(x: f64) {
   print!("{}", x);
}

fn add(x: f64) -> f64 {
    x + 1.0
}

fn subtract(x: f64) -> f64 {
    x - 1.0
}

struct Foo {
   data: f64,
   special: fn(f64) -> f64
}

impl Foo {
    fn new(data: f64, special: fn(f64) -> f64) -> Foo {
        Foo { data, special }
    }
}

fn main() {
    let mut foos = [Foo::new(4.0, add), Foo::new(2.0, subtract)];

    for foo in &mut foos {
       common_behaviour(foo.data);
       foo.data = (foo.special)(foo.data);
    }
}

What is most idiomatic way to handle this situation?

Upvotes: 2

Views: 98

Answers (1)

Matthieu M.
Matthieu M.

Reputation: 300139

Looking at:

fn main() {
    let mut foos = [Foo::new(4.0, add), Foo::new(2.0, subtract)];

    for foo in &mut foos {
       common_behaviour(foo.data);
       foo.data = (foo.special)(foo.data);
    }
}

I see a Command Pattern struggling to emerge, and Rust is great at expressing this pattern, thanks to enum:

enum Foo {
    Add(f64),
    Sub(f64),
}

impl Foo {
    fn apply(&mut self) {
        match self {
            Foo::Add(x) => {
                Self::common(*x);
                *x += 1.0;
            },
            Foo::Sub(x) => {
                Self::common(*x);
                *x -= 1.0;
            },
        }
    }

    fn common(x: f64) {
        print!("{}", x);
    }
}

And your example becomes:

fn main() {
    let mut foos = [Foo::Add(4.0), Foo::Sub(2.0)];

    for foo in &mut foos {
       foo.apply();
    }
}

Upvotes: 1

Related Questions