Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Rust and Haskell both use something called existential types to implement their versions of Go's interfaces. An existential type is basically a type meaning "any type that implements the given type-class/trait/concept", so you can use to create an array of different types that all satisfy the same contract. Type-classes and traits are roughly Haskell and Rust's equivalents of Go's concepts. Interestingly, Go contracts actually support a feature missing from Rust: multi-param contracts; contracts that refer to two or more types.


... and Rust does unify both of these use cases with traits, that is, what you're referring to is a "trait object", which is the same thing as Go with regards to interfaces. The difference is when you use a trait to bound a generic, in which case they get monomorphized and become "direct calls" in the OP's parlance.

I haven't re-read it recently, but https://softwareengineering.stackexchange.com/questions/2472... did contain a pretty great explanation of how these things relate.

It's also worth noting that we also saw some confusion from people not realizing these two use cases for the same feature, and so added some syntax to differentiate the two.


"An existential type is basically a type meaning "any type that implements the given type-class/trait/concept", so you can use to create an array of different types that all satisfy the same contract."

I can't speak for Rust, but when I was first learning Haskell I was bitten by believing it worked that way, but it doesn't:

    Prelude> :t sum
    sum :: (Num a, Foldable t) => t a -> a
    Prelude> sum [1::Int, 2::Int]
    3
    Prelude> sum [1::Float, 2::Float]
    3.0
    Prelude> sum [1::Float, 2::Int]

    <interactive>:25:16: error:
        • Couldn't match expected type ‘Float’ with actual type ‘Int’
        • In the expression: 2 :: Int
          In the first argument of ‘sum’, namely ‘[1 :: Float, 2 :: Int]’
          In the expression: sum [1 :: Float, 2 :: Int]
You can kinda make it work by wrapping it behind yet another type that basically boxes the different underlying types, but that causes its own problems (including, for instance, the fact that no amount of boxing will make this example work; sum is also demonstrating why Haskell won't do this). Basically in Haskell, either do it a different way (and there's usually a different way that will make it work better), or go to full on heterogeneous lists that don't even need the values to be the same type like HList, but unless I've missed an implication of one of the more recent type extensions, you can't have a simple container like this that has heterogeneous types based on their interface. (I know you can make complex ones, because I have myself, but not the way I'm fairly sure you meant.)


In Rust, you do need some kind of indirection here; it is often described as a Box, but it can be any kind of pointer type.




Consider applying for YC's Winter 2026 batch! Applications are open till Nov 10

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: