Vector of structs?

As in Vector of classes vs Vector of instances.

Suppose I have

struct A {
    ...
}

struct B {
   ...
}

fn main() {
    let my_struct_vec = vec![A, B]
}

What I would like to be able is to iterate over this vector of structs (as I would in Python over a list of classes) and then iterate over some other collection and try and initialise each of those structs with each of the values from the second collection, keeping the instances that initialised successfully:

fn main() {
    let my_struct_vec = vec![A, B];
    for symbol in "my_string" {
        for my_struct in my_struct_vec {
            if let Some(instance) = my_struct::new() {
                // do something here
            }
        }
    }
}

Is it possible?

I will also try to look into using a factory

This is not possible in the same way as e.g. for Python, since Rust is a statically typed language, so types only exist at compile-time and can't be put into a vector like that.

On the other hand, it is possible to do similar things, e.g. using macros, or fancy type-level-“magic”, or using dynamic things like trait objects and function pointers. Which, if any, of these approaches would be the best depends heavily on your actual use-case. Since your code example is not a very concrete/practical/complete one, but more of a Foobar-style code example to illustrate and ask about an attempted solution rather than an “actual” problem, it’s hard to give a good suggestion without more context.

2 Likes

Thank you. I am trying to get my feet wet, and I am basically trying to reimplement some of my python code in Rust.

I am trying to write a calculator, where the first step is to parse user input into tokens, e.g, numbers, operations, blank space, etc:

trait Token {
    fn append(&mut self, symbol: String) -> bool;
    fn new() -> Self where Self: Sized;
}

struct Operation {
    symbols: Vec<String>,
}

impl Operation {
    const ALLOWED_VALUES: &'static [&'static str] = &["*", "/", "+", "-", "**"];
}

impl Token for Operation {
    fn new() -> Self {
        Operation {symbols: vec![]}
    }

    fn append(&mut self, symbol: String) -> bool {
        // if this symbol makes a valid operation together with the previously added ones, add it to self.symbols, return true, otherwise return false 
    }
}

Then I want to iterate over the symbols of the user input and try to add it to all of the possible tokens. At the first iteration, I try to add it to all tokens known to my programme, and keep only those ones that could add the symbol to themselves, then I will add the symbol to the remaining tokens and remove the ones that couldn't consume the symbol. When I'm left with one token, and that token stops accepting new symbols, I will deem it complete.

For that I need a way to produce new token instances. I am trying to use traits, but I run into errors:

trait Factory<T> {
    fn make(&self) -> T;
}

struct OperationFactory {}
impl Factory<Operation> for OperationFactory{
    fn make(&self) -> Operation {
        return Operation { symbols: vec![] };
    }
}

fn main() {
    let known_token_types: Vec<dyn Factory<dyn Token>> = vec![SpaceFactory{}, OperationFactory{}];

}
the size for values of type `dyn Factory<dyn Token>` cannot be known at compilation time
the trait `Sized` is not implemented for `dyn Factory<dyn Token>`rustcE0277
mod.rs(400, 16): required by a bound in `Vec`

As for the fancy stuff, I am not ready for that yet )

Alright, the approach you’re trying almost works. Just you’ll need to work with Box<dyn Trait> instead of dyn Trait directly in most cases because trait objects can only exist behind some kind of pointer/indirection (i.e. you’ll always need Box when you want an owned trait object, but things like &dyn Trait or &mut dyn Trait can work without the Box).

trait TokenFactory {
    fn make(&self) -> Box<dyn Token>;
}

struct OperationFactory {}
impl TokenFactory for OperationFactory {
    fn make(&self) -> Box<dyn Token> {
        Box::new(Operation { symbols: vec![] })
    }
}

fn main() {
    let known_token_types: Vec<Box<dyn TokenFactory>> =
        vec![Box::new(SpaceFactory {}), Box::new(OperationFactory {})];
}

Instead of a factory trait, you could also use function pointers and closure syntax:

fn main() {
    let known_token_types: Vec<fn() -> Box<dyn Token>> =
        vec![|| Box::new(Space::new()), || Box::new(Operation::new())];
}

Note however that a more idiomatic approach would often be to work with enums instead of trait objects; admitted that would probably imply more re-structuring compared to your python code.

2 Likes

Note that there is also the HList data structure provided by the frunk crate, which is kind of a type level linked list.
Whether that's appropriate for your use case, I do not know.

It probably won’t be of much use here— HLists generally require the sequence of types to be known at compile time, which means that it can’t be dependent on input. This doesn’t really work for parsing problems for obvious reasons.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.