How to return a generic struct from a fn?


#1

Given this sample code, how can i return a random struct from a single fn?

struct CommandLogon;
struct CommandHeartbeat;
struct CommandData;

fn parse() -> ? {
    // the return can be on of the structs
}

I tried lots of workarounds on my code but, every time, i stop when i need to do this case.


#2

Playpen:

enum Command {
    Login,
    Heartbeat,
    Data,
}

fn parse() -> Command {
    Command::Login
}

#3

I think it’s important to show that the enum variants can contain data, as in

enum Command {
    Login(user: User, password: Password),
    Heartbeat,
    Data(payload: ...),
}

#4

I was expecting something more generic like this in java:

public enum CommandType {
    Logon,
    Heartbeat,
    Data,
    Invalid
}

public abstract class Command {
    protected static CommandType detect(String message) {
    }
}

public class CommandLogon extends Command {
}

public class Client {
    public Command parse(String message) {
        Command.detect(message);
        return new CommandLogon();
    }
}

But if i cant do this in rust, its ok too, just need more abstractions to get where i need.


#5

Hi there!

FYI: When I switched from Java to C++ I also write lots of abstract classes (“interfaces”) because that was the only kind of abstraction I was familiar with. But I have since grown and welcomed generic programming as part of my toolset. And now with Rust, I learned about how cool enums actually are. Nowadays, I almost never write abstract classes in C++. I’m not saying that they are not useful. But I’m saying that they are not as useful as you might think. The “Java style” is not necessarily the best one when imported into a very different language like Rust.

If you really need the set of commands to be “open” then trait objects are probably the way to go which comes very close to what you believe you want :wink:

pub trait Command {
    fn blah(&self) -> String;
}

struct This;
struct That;

impl Command for This { fn blah(&self) -> String { "this".into() } }
impl Command for That { fn blah(&self) -> String { "that".into() } }

fn parse(s: &str) -> Option<Box<Command+'static>> {
    match s {
        "this" => Some(Box::new(This)),
        "that" => Some(Box::new(That)),
        _ => None
    }
}

fn main() {
    let x = parse("this").unwrap();
    let y = parse("that").unwrap();
    println!("{}", x.blah());
    println!("{}", y.blah());
}

But I would try to avoid this kind of programming. It tends to get ugly if you later need to switch/match on the dynamic type. in such a case an enum-based variant type like already shown is probably the better solution.


#6

You can create a Command trait, implement each struct and use trait object.

trait Command {
}

struct Logon;
struct Heartbeat;
struct Data;

impl Command for Logon {
}

impl Command for Heartbeat {
}

impl Command for Data {
}

fn parse(x: &Command) {
}

fn main() {
    let x = Logon;
    
    parse(&x as &Command);
}

Of course, it’s not the suitable way of doing such a thing. Prefer using Algebraic Data Types (enums) instead of trait objects.

edit: Oh, sellibitze answered first.


#7

thx for the help guys!