|
|
Log in / Subscribe / Register

How does this compare to "Accept interfaces, return structs" in Go?

How does this compare to "Accept interfaces, return structs" in Go?

Posted Apr 25, 2024 12:40 UTC (Thu) by davecb (subscriber, #1574)
Parent article: Existential types in Rust

I use interfaces to tell an implementation "here's what you need to do" in a program that has a bazillion plugins to do related things.
I then return a struct that has a concrete structure, not visible to the caller, that contains the type the implementation needs.

Off-topic, I laughed out loud when I read the definition: existential versus universal quantifier, backwards E versus upside-down A .
I trained as a logician, but it's been so long now that I thought "existential" was being used in the sense we use in "existential risks", ie, risks that could cause the collapse of human civilisation. Types that could cause Rust to disappear in a little cloud of smoke (:-))


to post comments

How does this compare to "Accept interfaces, return structs" in Go?

Posted Apr 25, 2024 14:00 UTC (Thu) by daroc (editor, #160859) [Link] (6 responses)

When I described the topic of the article to my mother, she suggested that "existential types" ought to refer to philosophers who sit around in cafés smoking very thin cigarettes and avoiding writing poetry. So it's clearly a term with many possible meanings. :-)

In regards to your question to how this compares to Go — I have never written any Go, so I can't say for sure, but it certainly sounds like the same technique from your description, possibly with less involvement from the type system. I think a lot of programming languages can express a pattern like this, it's just a matter of the tradeoffs between runtime overhead, safety, and type-system complexity.

How does this compare to "Accept interfaces, return structs" in Go?

Posted Apr 25, 2024 16:02 UTC (Thu) by rds (guest, #19403) [Link] (5 responses)

Not a Go or a Rust person but I think the distinction is mainly going to be compile time application of a concrete type in Rust vs runtime in Go.

How does this compare to "Accept interfaces, return structs" in Go?

Posted Apr 25, 2024 16:08 UTC (Thu) by davecb (subscriber, #1574) [Link] (4 responses)

The Go structs are checked at compile-time, as are the interfaces, so it does have type safety. Go dislikes runtime stuff almost as much as Rust does (:-))

How does this compare to "Accept interfaces, return structs" in Go?

Posted Apr 25, 2024 16:10 UTC (Thu) by rds (guest, #19403) [Link] (3 responses)

Yes, but does it resolve the interfaces to a specific instance type at compile time? The article states that Rust does/will do this.

How does this compare to "Accept interfaces, return structs" in Go?

Posted Apr 25, 2024 16:14 UTC (Thu) by davecb (subscriber, #1574) [Link] (2 responses)

Yes, it does

How does this compare to "Accept interfaces, return structs" in Go?

Posted Apr 26, 2024 0:56 UTC (Fri) by wahern (subscriber, #37304) [Link] (1 responses)

> > Yes, but does it resolve the interfaces to a specific instance type at compile time? The article states that Rust does/will do this.
>
> Yes, it does

This doesn't sound right with respect to either Go or Rust. In Go interfaces are statically resolved to concrete types only opportunistically. It can't always do this because a function might choose which concrete implementation to return based on runtime logic.

IIUC, the same is true of Rust. In Rust you can return a `impl Trait` directly only if there's a single concrete implementation it could possibly return, otherwise it has to be boxed. Named existential type are just as strict: a named existential type can only have a single concrete implementation, period. The concrete type appears to be inferred from the usage context, but IIUC if the second context can't be coerced to the same concrete type as in the first context, then compilation will fail.

It couldn't be any other way, at least as a practical matter. (Though I could imagine a system of symbolic execution where the compiler generates code for all the alternations, but if you thought compile times were crazy now....) People seem to get all exited about various Rust features based on the fancy technical jargon and assume Rust is performing something magical. But people's memories are short, apparently. IIUC, existential types here are quite useful largely as a syntactic sugar when writing implementations, but otherwise more a boon to the compiler in circumscribing the complexity of the type resolution expected of it. The hype over what it offers async code seems overblown; it's not going to magically make opaque interface-like types with multiple distinct concrete implementations magically statically dispatchable when they're returned from a function which could return one or the other dynamically.

How does this compare to "Accept interfaces, return structs" in Go?

Posted Apr 26, 2024 13:56 UTC (Fri) by tialaramex (subscriber, #21167) [Link]

Yeah, I think this feature in Go promises only for example T = ∀X { a: X; f: (X → int); } whereas in Rust you're promised T = ∃X { a: X; f: (X → int); }

That is, in Go they're saying this has *any* type which has the specified property and it might vary, while in Rust we're saying that while we won't tell you what type it is, we promise there is *some* specific type which has that property.

The Rust compiler will learn what that type is, but it doesn't need to spell it, for the compiler this being "the type you get when you compile the closure on line #124 of this file" is fine, it's type #10295 but humans want names, both in terms of being able to understand what's going on, and in Rust because they are literally required by the language to spell the name of a type in the function signature, so hence without Existential types you cannot do this in Rust.

In C++ you don't have to spell names of types in most places, you can talk about types in terms of other things, for example decltype(a + b) is just "The type of the result if you were adding a and b together" but they still have the same distinction Rust has and for the same reason - it's expensive to handle that any type case, in a language like Go it's no cheaper when this happen but you're used to the language just not telling you the price of anything - it's girl menus at a posh restaurant, is the salad cheaper than the lobster? Who knows, the prices are on the menu the guy has.

How does this compare to "Accept interfaces, return structs" in Go?

Posted May 20, 2024 23:31 UTC (Mon) by riking (subscriber, #95706) [Link]

This is basically the same as returning a private struct in Go, except you're forced to specify the interface it implements, and the caller is filtered to just that interface. The caller can't downcast it to discover other interfaces it has (unless you include std::any::Any in the interface).

Rust doesn't allow you to return private structs (except via the public-in-private trick, the Go equivalent would be mypkg/internal.SomeType - you can't name the type but you can call all its methods), which is why this work is needed.


Copyright © 2026, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds