Depending on how you are going to use your testing_generic_struct() function it might be enough to wrap the desired methods up in a trait, implement it for MyStruct<&'static str> and MyStruct<u32>, and return Box<dyn MyStructTrait>.
Otherwise you could create a bespoke enum where one variant holds MyStruct<&'static str> and the other holds a MyStruct<u32> (see @alice's answer) .
At the end of the day you can only return one type because MyStruct<&'static str> is represented very differently to MyStruct<u32> in memory, so we need a way to treat them the same.
It is important to realize that when generics are involved, they must always be specified, the concrete type must be known at compile time, and it must be a single specific type. You cannot use MyStruct as a type because it is not a type. To get a type, you need something like MyStruct<i32> or MyStruct<String>.
Enums is one way to create a type that can be one of several other types.
Another way to do that is trait objects, which are what you get when you write dyn SomeTrait with the dyn keyword in front of the trait. The trait object (with dyn in front) and the trait (without dyn) is not the same concept — the trait object is a type, the trait is a trait. Any reference to a type that implements SomeTrait can be turned into a reference to a dyn SomeTrait. (Trait objects can only exist when its behind a reference or smart pointer, e.g. &dyn someTrait or Box<dyn SomeTrait>.)
Thank you @alice. I did try this approach, but I then need to extract the value out of the enum and it all seems so very messy and cumbersome, and I'm sure it would not be considered the "way to do it in Rust".
fn main(){
let s1 = testing_generic_struct(1);
match s1.value {
IntOrString::Int(v) => println!("{}", v),
}
}
// >> 42
You can define methods on the enum and put the match inside the method. Then the code calling the methods would be cleaner. For example, Option and Result has lots of convenience methods.
Besides that I don't think I can give further advice for how to make this toy example cleaner. If you have a more specific problem, I may be able to suggest a better alternative.
Thank you @Michael-F-Bryan.
I have tried @alice 's answer. It works, but feels very cumbersome. I shall give yours a spin, but again, it seems very verbose for what I'm trying to achieve. In essence I just want to iterate over a collection (String) and create a struct that holds a value which may be a u32 or a char depending on the value returned by the iterator.
Thanks again @alice , both you and @Michael-F-Bryan have answered my question - namely that I can't do what I was hoping to do.
My knowledge of Rust is just too small at the moment to really understand the "Rust way to do things".
I'm slowly working my way through The Book and Rust in Action. The compiler helps too, but I'm finding it a frustrating language to learn.
I'm not actually sure what you were hoping to do. If you have a thing that could be either u32 or String, you would still need some way to branch on which type it actually has and choose the behavior you want for each situation.
@alice , I was just experimenting, but trying to duplicate a basic loop from a Python programme: Let's Build a Simple Interpreter that iterates over a string and returns a Token based on the character found.
I'd worked out how to use a peekable iterator over the string, pass the iterator around and gather multiple contiguous digits together and convert them to an integer. My next experiment was to see if I could duplicate this part of the code and create Tokens with a value field that represented an integer or a character, as above. All fun and part of the learning process.
What you're going to do with this value afterwards? Note that char and u32 can be converted into one another - with as operator and char::from_u32 method, correspondingly (the method is fallible, since not every u32 is valid as char), so you might be able to use only one of them, converting when necessary.
This definitely looks like a use-case for an enum. The reasoning is simple: you probably wouldn't want to have only these two kinds of tokens, and any further extension would make other approaches quite cumbersome.
The Token(INTEGER, int(current_char)) and Token(PLUS, current_char) thing that the python code is doing is essentially how you simulate enums in languages without enums. Creating a Token enum is the standard approach here.
Ah, interesting. I thought that enums were only for a collection of constants, and that structs were used for Object/Record -like entities
At the moment I have Token as a struct, the TokenType as an enum, and, if I'm following your advice correctly, I have a further enum for the value Type - TokenValue.
Would the Rust approach then be to have a single Token enum, with each field as a type? Similar to the IpAddrKind example from p.99 in The Book?
Then, to dig out the actual value, create a match clause?
#[derive(Debug)]
enum Token {
Integer(u32),
Character(char),
EOF(u32),
}
fn main() {
let t1 = Token::Integer(42);
let t2 = Token::Character('+');
let t3 = Token::EOF(0);
match t3 {
Token::Integer(v) => println!("{}", v),
Token::Character(c) => println!("{}", c),
Token::EOF(_) => println!("That's all folks!"),
}
}
Yes, your last snippet is way more idiomatic rust. The first snippet would make it possible to create a token where the value and token type doesn't match up, and Rust is all about eliminating that kind of situation at compile time.
Also note that each enum variant can have a different number of parameters, so you do not need EOF to carry the dummy u32 like the Python code carries a dummy None , this will do: