1

I'm fairly new to Rust, and as a project to make my hands more used to it, I decided to implement a library about category theory and functional programming in general. Long story short, there is a trait called Monad:

pub trait Monad {
// Associated types and functions here
}

And in rust, Options are Monads, so I went like this:

impl<X> Monad for Option<X> {
// Implementation here
}

Everything's working properly so far. I decided to add a special Monad called Identity Monad, which is a famous and handy one:

type Identity<A> = A;

impl<X> Monad for Identity<X> {
// Implementation here
}

And right there, I get this error: conflicting implementations for Option<_>. Now I have read this SO post, and it makes sense for that use case, but about mine, as far as I can think through, since Identity is a type (not a trait) it may never be the "Option". Can anyone let me know about why's that happening and what I can do for it?

Kevin Reid
  • 37,492
  • 13
  • 80
  • 108
AminMal
  • 3,070
  • 2
  • 6
  • 15

2 Answers2

4

They conflict because Identity<Option<X>> is the same type as Option<X>.

type does not define a new type, it defines an alias for an existing type; Identity<X> is just another name for X.

You might be interested in the newtype pattern where you create a single-struct field to define a new semantic type wrapping another:

struct Identity<X>(X);

Now Identity<X> is a different type from X, and the implementations would no longer conflict.

If you decorate Identity with #[repr(transparent)] then it's even possible to safely transmute &X to &Identity<X>, which might be useful depending on the members of Monad.

cdhowie
  • 158,093
  • 24
  • 286
  • 300
1

As has already been mentioned, the problem is that type defines an alias, a new name, for an existing type, so Identity<T> = T and therefore your impl<X> Monad for Identity<X> is equivalent to just impl<X> Monad for X.

I would like to provide some additional context that may be useful, presuming that you (or some other reader with the same question) are a Haskell programmer.


In Haskell, there are 3 kinds of type declarations: data, type, and newtype. Here is how they correspond to Rust:

  • Haskell's data directly corresponds to Rust's enum — it declares a new distinct type that holds data and can have multiple constructors (Haskell) / variants (Rust). Rust also offers the struct which does not have variants, and in exchange can have private fields and access with dot notation (somewhat like Haskell record syntax).

  • Haskell's type directly corresponds to Rust's type — they define aliases for existing types.

  • Haskell's newtype has no direct analogue in Rust. newtype defines a new distinct type which has exactly one data field, and it is expected that this type is represented at run time exactly like that field (with no additional indirection, and no additional laziness).

    However, in Rust, the default assumption is that a struct has no additional cost and no different behavior than just using its field or fields. (And there is no laziness, so no laziness distinction.) So, when Rust programmers speak of “the newtype pattern” they mean the pattern — not the language feature — of declaring a struct with one field to introduce a distinct type.

    You can further ask for a guarantee that the newtype is represented exactly the same as the old type, by using #[repr(transparent)]. This is only necessary if you intend to access the memory of one type as if it were the other, using unsafe code or a library (such as bytemuck) that does it for you.

    #[repr(transparent)]
    struct Identity<X>(X);
    

    This is the maximal form of the “newtype pattern” in Rust.

Kevin Reid
  • 37,492
  • 13
  • 80
  • 108