[HN Gopher] Trivia About Rust Types
___________________________________________________________________
Trivia About Rust Types
Author : todsacerdoti
Score : 91 points
Date : 2022-06-07 10:59 UTC (12 hours ago)
(HTM) web link (www.thecodedmessage.com)
(TXT) w3m dump (www.thecodedmessage.com)
| gameswithgo wrote:
| I really enjoy some of the optimization details like when the
| memory overhead of Option<> can be elided completely. Other
| languages with Options will actually toss data up on the heap
| that doesn't need to be because it is in a Option and, or at the
| very least always take up a byte with the tag.
|
| Details like this let you use the safer and more ergonomic
| solutions by default rather than having to worry if they are
| worth the overhead.
| tialaramex wrote:
| Indeed. Also more broadly Rust's types _do not exist_ at
| runtime, and so you get the New Type idiom. If we care about
| the difference between a Row and a Column in the program, we
| can create distinct Row and Column types, and Rust won 't allow
| you to use a Row when you need a Column, even if they're both
| "just integers", yet at runtime there's no cost for this, if
| the obvious representation of Row and Column is a 32-bit
| integer that fits nicely in the general purpose registers and
| takes up 4 bytes of RAM, that's how they're both represented at
| runtime.
|
| You can do a lot of this in languages like C++ but there are
| some pernicious limits that Rust didn't have e.g. C++ can't
| conceive of Zero Size Types.
| RcouF1uZ4gsC wrote:
| > Did you know that Option<T> implements IntoIterator yielding
| 0/1 elements, and you can then call Iterator::flatten to make
| that be 0/n elements if T: IntoIterator?
|
| I thought this was so cool, being able to treat Option like a 0/1
| collection.
| pornel wrote:
| Personally I dislike this (Result has it too), because it gives
| bizarre errors if you forget to unwrap a result:
| let collection = get_stuff(); for item in collection {
| }
|
| This can unexpectedly give you one item of iterable `Result`
| instead of items of the collection if `get_stuff()` returns a
| `Result<Vec>` or `Option<Vec>`.
|
| Such mistake won't make it to production thanks to strict type
| checking, but it manifests in surprising and misleading error
| messages that the `item` doesn't have methods you expect it to
| have, rather than pointing to the true cause of `get_stuff()`
| not having error handled.
| tialaramex wrote:
| I can't say I've ever run into this, but it sounds like you
| have. You might find there's a common pattern which
| distinguishes this mistake from typical scenarios where you
| did something else wrong and the compiler can use that to
| provide more helpful errors in this case.
|
| Seems like, we do for x in Result-or-Option<T> and T is
| IntoIterator, and then we treat x as if it is the thing T was
| an iterator for, rather than T. That's detectable.
|
| So, worth taking some examples of code where you did this,
| and writing up a report that the compilers team can look at.
| If you've got some idea what you're doing you might well even
| find Rust's error reporting code readable enough to dive in
| and try some fixes yourself.
| Arnavion wrote:
| I've seen users come asking for help about it on IRC a
| bunch of times, so it's probably a pretty common typo, at
| least among beginners. In retrospect it might've been
| better if there was an explicit conversion to the
| (Into)Iterator type instead of being impled on
| Option/Result itself.
|
| https://www.arnavion.dev/blog/2020-04-07-rust-the-
| unfortunat...
| 3836293648 wrote:
| I've never run into this because I always write out my
| `.iter()`s and `.into_iter()`, but this is why I don't think
| loops should accept `IntoIterator` types, just actual
| `Iterator`s so you're forced to type it out
| tialaramex wrote:
| This just adds verbosity - there's a blanket implementation
| of IntoIterator on Iterator so people would just learn to
| write
|
| for x in y.into_iter()
|
| and presumably curse 3836293648 who made them do this extra
| typing for no purpose.
|
| It doesn't, as you seem to imply fix the parent's problem,
| because both the container they thought they were getting,
| and the Option or Result they were actually getting,
| implement IntoIterator so they'll call into_iter() on it.
| afdbcreid wrote:
| There is a warn-by-default Clippy lint for that:
| `clippy::for_loops_over_fallibles`. However, being an
| `IntoIterator` is important for things like flattening and
| other iterator adapters.
| ridiculous_fish wrote:
| A minor rant about std::ptr::NonNull: this wraps a *mut T, and
| there is no equivalent for *const T. So if you want to use the
| optimization for a pointer-to-const, you must cast away
| constness, and just be careful to never mutate the pointee. In
| practice that's exactly what the standard library does.
| Rusky wrote:
| There's a bit of a sentiment that the `*mut T`/`*const T`
| distinction is more unhelpful noise than an actually useful
| feature. They can be freely cast between each other - `*mut T`
| to an immutable object is totally legal - and thus (much like C
| and C++'s const) don't really mean anything about the thing
| they point to. Further, writing through one or converting back
| to a reference is already `unsafe`, so even using them as a
| lint doesn't add much.
|
| AIUI that is why NonNull only has one variant- it's just easier
| to have one kind of pointer. But interestingly, even though
| `NonNull`'s API is written in terms of `*mut T`, it is
| _implemented_ in terms of `*const T`: https://github.com/rust-
| lang/rust/blob/4ca19e09d302a4cbde14f.... This makes it
| covariant (`NonNull<T>` for `T: 'long` is a subtype of
| `NonNull<T>` for `T: 'short`), while `*mut T` is invariant,
| since this is the more common mode, and also the more flexible
| option that can be restricted back down to invariance when
| necessary.
|
| So NonNull is sort of a re-setting of the defaults: the unsafe
| mutability of `*mut T` (since people wind up needing to cast
| covariant `*const T`s to `*mut T` to write through them a lot
| anyway), with the covariance of `*const T`.
| joshfee wrote:
| Have only just started reading the article, but its really
| annoying that there are no hyperlinks to the referenced
| types/methods.
|
| > See the debug_* methods on it.
|
| Sure... please point me to them
| DonaldPShimoda wrote:
| To be fair, these are all excerpts from a Twitter thread where
| the tweet author was just trying to informally drop some
| knowledge to people who were asking about the types in question
| and therefore probably know where to go for the documentation.
|
| Those links would be a great addition to the article, though.
| tialaramex wrote:
| For example:
|
| https://doc.rust-lang.org/std/fmt/struct.Formatter.html#meth...
|
| The entire Rust standard library documentation (like most
| documentation for Rust software, it's built with the same
| tools) has a Search UI at the top.
|
| https://doc.rust-lang.org/std/
| Robin_Message wrote:
| Interesting that a language that targets embedded (amongst other
| things) has a default vector allocator for space for 4 entries of
| objects between 2 and 1024 bytes. I can imagine that being
| wasteful for code that has a lot of very short lists of non-
| trivial objects (e.g. something like configuration options).
|
| Although I expect it is overridable in some way but I don't know
| enough Rust to know how.
| nynx wrote:
| Vec and allocators aren't usually used for embedded rust since
| the std library can be disabled. (Though the _core_ library is
| still accessible)
| gameswithgo wrote:
| You can specify the capacity on creation:
| Vec::with_capacity(foo)
|
| There are also alternative vector like types for different use
| cases, and you can roll your own.
| __s wrote:
| There's also crates to avoid heap allocations for usually
| small vectors: tinyvec, smallvec, arrayvec
|
| Note: gameswithgo you seem to be shadow banned. Your comment
| history looks alright tho. You may want to email
| hn@ycombinator.com asking to have that fixed _(edit: found
| the culprithttps://news.ycombinator.com/item?id=30732886)_
| rascul wrote:
| There's some good information here but I find it slightly
| annoying that each item starts with "Did you know". Also, some
| examples would be nice. Descriptions are good but it can take a
| few minutes for me to visualize in code what is explained.
| sam_bristow wrote:
| I think both of those issues are because this is a fairly
| straight forward transcription of the original Twitter thread.
|
| I agree that it would be nice if they'd flesh out some of the
| items with example code though.
| rascul wrote:
| Ahh ok. I didn't realize it was a Twitter thread. I guess I
| didn't actually look at the article title.
| tialaramex wrote:
| When Jon did the initial tweets, almost immediately non-
| Twitter users were like "Please transcribe this somewhere
| else" and there was a Reddit thread, now this is an actual
| article.
|
| I think Jon just intended it as a bit of fun, and so
| Twitter felt appropriate. After all if there's important
| stuff to be communicated about a type it should be in the
| actual documentation. If you want to learn about types
| entirely by reading Tweets that seems like a weird and
| probably bad learning style. See also: learning ballet
| entirely by reading translated poems about ballet.
| tialaramex wrote:
| ControlFlow https://doc.rust-
| lang.org/core/ops/enum.ControlFlow.html is awesome.
|
| Aside from its actual purpose (as part of making ? less magic,
| described in this trivia list) it's good to agree how to talk
| about control flow in the type system. If two unrelated pieces of
| software both care about control flow, in a lot of languages
| they'd both make up their own ways to signal "keep going" versus
| "halt". But since core::ops::ControlFlow exists in Rust it makes
| sense to use that, and so you spend less time writing adaptors.
| This is one of the main purposes of a standard library (notice
| this is in _core_ not just _std_ and so it 's available even in a
| tiny embedded device with no allocator if you want it)
___________________________________________________________________
(page generated 2022-06-07 23:02 UTC)