Live data from Hacker News

Never patterns, exhaustive matching, and uninhabited types in Rust

smallcultfollowing.com

51–60 of 83 posts

Re: Never patterns, exhaustive matching, and uninhabited types in Rust

#51
post #3

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 if there is no error to return. Why not just return a String?

I 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");
    }

Re: Never patterns, exhaustive matching, and uninhabited types in Rust

#52
post #44

Earlier quoted context omitted.

Consider 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…

This 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.

I think ones of the design books I read years ago talked about a piece of building infrastructure that “could not fail” so they incorporated it directly into the concrete pour.

Of course it failed, and it was a royal pain to get to the bits that failed because they were embedded in concrete that was probably holding the building up.

You build for robustness because you might be wrong, the new guy might not understand, or a silicon isotope might decay at just the wrong time and flip a bit. Circuits are analog. They just pretend very very hard to be binary. Weird shit can happen that shouldn’t be possible.

Re: Never patterns, exhaustive matching, and uninhabited types in Rust

#53
post #35

Earlier quoted context omitted.

Putting things on satellites might mean very different things. There is close to zero chance of anything critical being in Rust. One company I worked on has not even move from (a subset of) C90 for critical components. No C99, no C11, no C++03 and no Rust. It is likely that a company may have software in Rust, even running in a satellite module (though unlikely), but that is different than actual satellite systems be…

The talk had a diagram, most components on it were in Rust, and the plan is to use more and more moving forward. Even critical stuff.

Happy to hear that, since I am not exactly in joy of using ancient languages.

However, from plans to actual usage takes years; specially for completely new projects using new languages, compilers, libraries, etc. in safety-critical stuff, that takes months just to validate.

Stating anything else is just either lying (marketing) or ignorance. Please do not do a disservice to your language by creating vacuous hype.

Re: Never patterns, exhaustive matching, and uninhabited types in Rust

#54

Earlier quoted context omitted.

A 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.

> 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 " 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?

Because there are generic APIs that return results as part of the contract. A good example is FromStr.

Re: Never patterns, exhaustive matching, and uninhabited types in Rust

#55

Earlier quoted context omitted.

A 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.

> 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 " 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?

[deleted]

Re: Never patterns, exhaustive matching, and uninhabited types in Rust

#56
post #53

Earlier quoted context omitted.

The talk had a diagram, most components on it were in Rust, and the plan is to use more and more moving forward. Even critical stuff.

Happy to hear that, since I am not exactly in joy of using ancient languages. However, from plans to actual usage takes years; specially for completely new projects using new languages, compilers, libraries, etc. in safety-critical stuff, that takes months just to validate. Stating anything else is just either lying (marketing) or ignorance. Please do not do a disservice to your language by creating vacuous hype.

That was also a significant part of the talk. Like any industry, some shops are conservative, and some aren’t. Pointing out that some places are more liberal than others isn’t lying.

Re: Never patterns, exhaustive matching, and uninhabited types in Rust

#57

Alright this is going to be a dumb question but... Every time I start a new personal project I say 'oh neat I can use Rust!' N hours later (where N is a large number) I realize Go is about as fast, much easier and more readable syntax, and so I use Go if I want something that isn't Node. I'm a web developer so maybe Rust just isn't useful for that, but is Rust purely a systems level language for making super low leve…

There are certainly different tasks for which different languages are suited, however for many tasks multiple languages are well-suited.

To paraphrase your question, you are essentially asking: "Why should I use language Y instead of X when X is fast enough for me, easier for me to read, and based on experience I like it better?" My immediate thought is that it sounds like language X is working quite well and aside from satisfying your personal curiosity there's no imperative to make that switch!

Re: Never patterns, exhaustive matching, and uninhabited types in Rust

#58
post #44

Earlier quoted context omitted.

Consider 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…

This 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.

> execution continues in the face of impossible values

I don't know about C++, but it may be doing something different from Rust here. In Rust, the ! type (which I pronounce "Never"), isn't just a case of "I, the programmer, don't think any value will exist here"; the compiler forces you to prove (just like every other type is a proof) that a value can't exist. The Never type can't be instantiated (it has no constructor), and functions that "return" Never must prove that they, er, never return; imagine a function whose body is just `loop {}` (or the stdlib function process::exit, which cannot return by dint of killing the callstack when the process dies).

Re: Never patterns, exhaustive matching, and uninhabited types in Rust

#59
post #52
post #44

Earlier quoted context omitted.

This 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.

I think ones of the design books I read years ago talked about a piece of building infrastructure that “could not fail” so they incorporated it directly into the concrete pour. Of course it failed, and it was a royal pain to get to the bits that failed because they were embedded in concrete that was probably holding the building up. You build for robustness because you might be wrong, the new guy might not understand…

I think this is conflating two different concepts, only one of which concerns uninhabited types. If the programmer "knows" that a condition is impossible, but can't prove it to the Rust compiler, then they can the `unreachable!` macro to signal their intent to the compiler; in this case the compiler knows that it cannot trust the programmer (because it has no proof) and so generates code anyway that will handle the impossible condition (in this case, by panicking, which is the typical Rust reaction to a signal that the programmer's assumed invariants have been violated).

On the other hand, the use of an uninhabited type means that the programmer has to prove to the compiler that the type is uninhabited, in the same way that declaring a function as returning a String requires the programmer to prove that the function actually does return a String. If we have a function that returns a String, the compiler does not generate any code to handle the case where that function returns an integer instead; that just doesn't make sense, we've used the type system to prove that can't happen. Likewise, it wouldn't make any sense for the compiler to generate code to handle the case where a function returning an uninhabited type returns anything at all.

Re: Never patterns, exhaustive matching, and uninhabited types in Rust

#60
post #3

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 if there is no error to return. Why not just return a String?

> 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/Mon...

Post reply on HN