5 ms·
Can someone explain why you'd want uninhabited types? > if you have to define an error type for some computation, but this particular computation can never fai
by vultour 8y ago
Can someone explain why you'd want uninhabited types?
> if you have to define an error type for some computation, but this particular computation can never fail, you might use an uninhabited type.
I don't see the reason behind returning a Result<String, Err> if there is no error to return. Why not just return a String?
- the8472 8y agoWhen you implement a generic trait that is designed to handle errors but your specific case is infallible then it is helpful to indicate that the error case can never occur. Marking that as uninhabited makes it easier for downstream consumers to just eliminate all code related to error handling for that concrete type.
- zebraflask 8y agoPlease don't flame me if this is an ignorant question, but how is uninhabited different from Javascript's undefined? That's what I would use undefined for in JS (very loosely speaking).
- Veedrac 8y agoYou can actually set a variable to `undefined` in Javascript. You can't set a value to an uninhabited type─there is no value that represents the type.
- zebraflask 8y agoI get it, so the concept is closer to declaring a variable and that's it, nothing further happens. Thanks.
- sacado2 8y ago"undefined" is a type that has exactly one possible value. An uninhabited type has no possible value.
- phyzome 8y agoAn example I can think of is Java's StringReader class. It implements the Reader interface, so it has to bear the "throws IOException" signature... but it's impossible for it to produce an I/O error, since it's not actually doing any I/O, just reading from a String. That's fine if you're using it via the Reader interface, but if you're working with StringReader directly it's a pain in the butt to deal with the non-existent exceptions. It sounds like never-patterns would help with that.
- reikonomusha 8y agoPerhaps you’re making use of a parametric type provided by a library, and you don’t have control over the structure except by filling in type parameters.
- ChrisSD 8y agoI've used `enum Void { }` as a placeholder for opaque FFI managed types that I pass between APIs but never inspect myself. Like Windows handles (`*const Void`) or data structures that are queried using an API rather than directly.
- nalllar 8y agoenum Void shouldn't be used for opaque types. See these: https://internals.rust-lang.org/t/recent-change-to-make-exhaustiveness-and-uninhabited-types-play-nicer-together/4602 https://internals.rust-lang.org/t/recent-change-to-make-exha... https://github.com/rust-lang-nursery/nomicon/issues/29 https://github.com/rust-lang-nursery/nomicon/issues/29 https://github.com/briansmith/ring/issues/416 https://github.com/briansmith/ring/issues/416
- ChrisSD 8y agoOh wow, I didn't know this. At one time at least it was recommended, which was why I did it.
- bjz_ 8y agoYeah, IIRC many years ago I added it in a document as a pattern for C APIs (I can't remember where though). Either other people were doing the same thing, or were influenced by that. Anyway, you should use `*const libc::void` these days! Sorry for the confusion.
- the_mitsuhiko 8y agoA string is the worst thing to return because it’s huge. Most people return () instead which is zero sized. However when returning ! the compiler can optimize the entire error handling away.
- deleted 8y ago[deleted]
- masklinn 8y ago> A string is the worst thing to return because it’s huge. Most people return () instead which is zero sized. I think what they're asking is "if you're returning a Result<String, Err>" but you're making the Err an unhinabitated type (so can't ever have an error) why not just return a String in the first place?
- the_mitsuhiko 8y agoBecause there are generic APIs that return results as part of the contract. A good example is FromStr.
- deleted 8y ago[deleted]
- fnord123 8y agoA string is only like 16bytes (length + pointer to memory in heap) or more if it's a fat pointer.
- danidiaz 8y agoTypes that represent producers of data often return a special result value at the end, of a type different from the values that are yielded. Say, a stream of integers that return a Boolean when it's exhausted. By choosing an unhabited return type, you can express that the stream never stops producing values by itself.
- deleted 8y ago[deleted]
- sasmithjr 8y agoConsider the Ruby Gem, Fog[0]. It is designed to abstract different services away so that developers can just program against Fog and pick/choose different service providers as necessary. It's possible that for specific implementations of the fog-service boundary, an error cannot occur that definitely can occur in other implementations. Uninhabitable types allow the implementer to tell the compiler "I cannot provide an error here", giving the compiler more information to work with when optimizing. For example, the compiler now knowing that the function call cannot return an error may make it so that the function does only return the string after optimization, and the compiler can also drop the error checking code in the caller because it's unreachable. [0]: http://fog.io/ http://fog.io/
- zlynx 8y agoThis kind of thing has been plenty annoying in C++ code. Things like switch, default, abort that used to catch RAM or CPU errors and provide useful information now get optimized away and execution continues in the face of impossible values. Removing checks for "cannot possibly happen" cases isn't usually worth the tiny time savings.
- Rusky 8y agoI'm all for leaving asserts in release builds, but leaving in dead code based on uninhabited types isn't gonna help you catch RAM or CPU errors. You'll just continue into the error branch with an impossible value instead. It's the wrong layer for that.
- saagarjha 8y agoThis isn't an assert, it's not a runtime check; it's done at compile time. You need to prove to the compiler that your code can never reach this point; common ways of doing so are by calling exit or inserting an unconditional infinite loop. If your underlying computational platform is broken, this isn't going to save you, but I don't think there's much you can do at that point.
- 8y ago
- a1369209993 8y agoI mostly use them as return types for functions that are guaranteed to either loop forever or kill the process: noreturn mainloop(State* s) { for(; s->valid ;s->itick++) do_step(s); die(70,"invalid internal state"); }
- runeks 8y ago> Can someone explain why you'd want uninhabited types? Uninhabited types are useful for expressing units. I'm writing an app that makes heavy use of a Haskell library[1] that implements currency units and exchange rates using uninhabited types (type-level strings). So, one dollar will have the type `Amount "USD"`, whereas one Euro will have the type `Amount "EUR"` (both will have a value of `1`). An exchange rate from Euros to dollars will have the type `ExchangeRate "EUR" "USD"` (the first type parameter being 'source' and the second 'destination'), and its value will be `1.14` (the current exchange rate from EUR to USD). A function for converting an amount in one currency into another currency unit will take two arguments: `Amount src` and `ExchangeRate src dst` and return an `Amount dst`. Also, in general, it can be useful to: 1. Remove an argument from a function 2. Create an uninhabited type that, at the type level, represents the values of this argument 3. Tag the function's return type with this uninhabited "phantom" type because now you no longer have to remember from which argument some return value was calculated/derived: it's right there in the value's type. [1] https://www.stackage.org/haddock/lts-12.6/safe-money-0.6/Money.html https://www.stackage.org/haddock/lts-12.6/safe-money-0.6/Mon...