Does rust have real sum types?

Hi, all.
I want to express the limit set of type for an associated type in one of my traits, but I got stuck.

trait A { type Inner }
trait Example { type Associated: String | A where Inner == Vec<_> }

I assume it is identical to Haskell's sum types and type families.

//something like this. I forgot most of haskel, sorry*
data A b = A b | C

Can I do it in rust?

The Haskell type

data A b = A b | C

is written in Rust like

enum A<B> {
    A(B),
    C,
}

where you can call the constructors like A::A(expr_of_type_b) and A::C, respectively.

Rust traits are roughly equivalent with Haskell's typeclasses instead.

1 Like

Ah, my apologies, I likely did mislead you with my haskel clunkiness. What I want is to put constraints on associated type such that it can be either String or other type that implement some trait. Note, that I don't want merely a tagged union.

You can't do that directly. If you want to unify types like that, make sure they implement the same traits. If you control the trait, you can just implement it for String. If you don't, make a subtrait, add an impl for String, and a blanket impl for all types implementing the supertrait.

So, something like this:

trait WithAssoc {
    type Assoc: Foo;
}

impl Foo for String {}

or this:

trait WithAssoc {
    type Assoc: SubFoo;
}

trait SubFoo: Foo {}

impl SubFoo for String {}
impl<T: Foo> SubFoo for T {}

FYI, you can implement traits and methods for enums, as well.

1 Like

Well, maybe. But I don't see how I would do this for conditional conformance, eg:

//recall my example
impl <T: Example> T where T::Associated == String {}

I'm confused. What are you trying to implement for T? Your above example is missing a trait.

It's

impl<T> SomeTrait for T {}
1 Like

Exactly. And incidentally, that's a 3rd different thing. I'm not sure what's really being asked anymore.

Please try to make your arguments without implying negative character traits on other people.

5 Likes

Hmm... I get Rust to do useful things all the time. Or I imagine I do. One of us is clearly crazy then :slight_smile:

2 Likes

You cannot implement arbitrary methods for arbitrary types. You have to declare the methods in a trait, first and then implement the trait for arbitrary types. The reason is fairly simple: Ambiguity. If two methods conflict, then you need to be explicit about which method you want to call.
SomeType::some_method(&mut some_type) or MyTrait::some_method(&mut some_type). If you implement the methods in the way you tried, you cannot be explicit.

1 Like

This is possible. I must ask though why you need this kind of restriction, in particular, what do you think you are able to do after you ensured that Example is only implemented with an Associated == String or Assiciated: A with Associated::Inner == Vec<T>. If you have any concrete use-case in mind that makes you think you need these kinds of condition, then please also state that use-case, otherwise we’re deep into XY problem land here.

Anyways, at the high risk of this “solution” not being of any help to what you actually want to achieve, here’s how you can express your constraints:

// the `sealed` mod is private!
mod sealed {
    pub trait Sealed {}
}
pub trait StringOrAWithInnerVec: sealed::Sealed {}

impl sealed::Sealed for String {}
impl StringOrAWithInnerVec for String {}
impl<T: ?Sized, S> sealed::Sealed for T where T: A<Inner = Vec<S>> {}
impl<T: ?Sized, S> StringOrAWithInnerVec for T where T: A<Inner = Vec<S>> {}

pub trait A {
    type Inner;
}

pub trait Example {
    type Associated: StringOrAWithInnerVec;
}

This setup does prohibit any user of your code from implementing Example impls that do not meet your requirements.

4 Likes