Limit a generic type to be either A or B

I want to implement a generic function which applies to 2 types (A and B). Is there any way to specify that a generic type is either A or B in Rust?

You probably want this function to take an enum, not a generic type.

2 Likes

well, have a look at this example:https://users.rust-lang.org/t/how-does-file-open-take-either-a-str-or-path-type/65974?u=mr.greenhand

It is a little bit inconvenient to use the function if I make it take an enum.

1 Like

It sounds like you're looking for Trait Bounds. Instead of limiting the generic to a type, you limit the generic type to something that implements a specific trait.

2 Likes

Trait bounds can be too generic sometimes. Suppose there are many types implementing a trait but the function is only intended to be called on 2 types implementing the trait. Is there any way to further limit the generic type?

@Cerber-Ursi's suggestion to use an Enum instead of a generic type is a very good one but I still want to know whether it's possible to do it via generic types. Or is there any related rfc?

One way is to use the sealed trait pattern. For example, see the byteorder crate, note that the ByteOrder trait is bound by the Sealed trait, which can not be implemented in third-party crates. It's possible because Rust allows to use private traits in public bounds (one can say it's an example of a bug evolving into a feature).

1 Like

It's possible to do this with an enum and From/Into and a sealed trait.

pub struct Alpha;
pub struct Beta;

pub enum AlphaOrBeta {
    Alpha(Alpha),
    Beta(Beta),
}

impl From<Alpha> for AlphaOrBeta {
    fn from(alpha: Alpha) -> Self {
        Self::Alpha(alpha)
    }
}

impl From<Beta> for AlphaOrBeta {
    fn from(beta: Beta) -> Self {
        Self::Beta(beta)
    }
}

mod sealed {
    pub trait Sealed {}
    impl Sealed for super::Alpha {}
    impl Sealed for super::Beta {}
}

pub fn foo<T: Into<AlphaOrBeta> + sealed::Sealed>(x: T) {
    let x = x.into();
    // rest of code here
}

// foo(Alpha) and foo(Beta) both work in addition to passing the enum.
// Nothing else can work due to the sealed trait.
5 Likes

This is awesome! It's exactly what I was hoping to achieve.

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.