Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Either type A or B in rust

Tags:

rust

A have a struct with attributes that could have different types

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct A {
    pub foo: B | C
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct B {
    pub bar: usize
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct C {
    pub bar2: usize
}

Is it possible in rust to achieve something like pub foo: B | C, so the type will be determined on runtime?

like image 885
chocolate cake Avatar asked Feb 23 '26 04:02

chocolate cake


1 Answers

Is it possible in rust to achieve something like pub foo: B | C, so the type will be determined on runtime?

The way to do this in Rust is with an enum storing items of either:

#[derive(Debug, Deserialize, Serialize, Clone)]
pub enum A {
    B(B),
    C(C)
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct B {
    pub bar: usize
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct C {
    pub bar2: usize
}

Here A can have one of two values: A::B which contains a value of type B (in a way similar to single-member tuple structs) or A::C which contains a value of type C. A::B and B should not be confused: the former is essentially a function while the latter is a type.

The basest way to get values out of enums is pattern matching:

fn foo(a: A) {
    match a {
        A::B(b) => println!("Got a B: {:?}", b),
        A::C(c) => println!("Got a C: {:?}", c)
    }
}

Since Rust enums are completely standard types you can also add utility methods to them, that is exactly how e.g. Option and Result work e.g.

impl A {
    fn b(&self) -> Option<&B> {
        if let A::B(b) = self { Some(b) } else { None }
    }
    fn c(&self) -> Option<&C> {
        if let A::C(c) = self { Some(c) } else { None }
    }
}

fn bar(a: A) {
    if a.b().is_some() {
        println!("Got a B!");
    } else {
        println!("Got a C :(");
    }
}

Now since you are deriving Serialize and Deserialize with this I assume you're also interfacing with an external system e.g. typescript or whatever.

In that case you will almost certainly have to customise the serialisation scheme of the enum: serde's default is external tagging which in my experience is never what you're looking for, because it's really not a scheme other languages tend to use for their (formal or informal) unions.

Internal tagging and untagged are much more common, with the odd adjacent tagging once in a (rare) while.

like image 167
Masklinn Avatar answered Feb 27 '26 03:02

Masklinn



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!