[HN Gopher] How Not to Learn Rust
___________________________________________________________________
How Not to Learn Rust
Author : gtirloni
Score : 23 points
Date : 2021-12-16 15:30 UTC (7 hours ago)
(HTM) web link (dystroy.org)
(TXT) w3m dump (dystroy.org)
| bko wrote:
| > But at some point in Rust, and it may come soon, you'll
| encounter a higher step and if you don't fight it with
| concentration and dedication, you risk not overcome it.
|
| Can any rustaceans give me an example of a "higher step" in rust
| would be?
| dystroy wrote:
| At some point, maybe not immediately, depending on what you're
| building, you'll have the borrow checker tell you that no, you
| can't borrow there, or that some construct that you feel safe
| isn't possible. And it will probably come with a big lot of
| such errors and you'll have to look for another design pattern.
|
| Until it clicks and starts to be natural, there might be a
| quite rough period.
|
| Until you know the essential traits, how to find them, and
| you're used to clauses on generics, you may also have some
| difficulties making sense of some generic code or making your
| own generic code work.
| stouset wrote:
| To expand on this, I think one of the biggest challenges of
| Rust right now is that the borrow checker and compiler
| enforce constraints at a "tree"-level that are indicative of
| "forest"-level design issues with your code. Once you learn
| to construct forests in a way that harmonizes well with these
| constraints, you'll very rarely run into these kinds of tree-
| level issues.
|
| But there needs to be more in the way of education that helps
| you understand the types of designs that the compiler is
| trying to push you towards.
|
| You _can_ generally get past things by simply `.unwrap()`ing
| and `.clone()`ing your way past compiler issues, but reaching
| for them too often prevents you from ever learning those
| (important) lessons given the current lack of better
| alternatives to learning them.
| bko wrote:
| From the article:
|
| > So before you dive, have an overview of the book (or
| books). Maybe don't read it fully, but make sure to have read
| all titles so that you know when to come back to it later.
|
| Could someone familiar with Solana smart contract development
| highlight which titles from 'the book' are most relevant? I
| imagine developing smart contracts is much more scoped down
| than most rust projects.
| ithkuil wrote:
| I've passed that hill behind me and I rarely have to fight
| the borrow checker. If you're familiar with the ownership
| model and learn the few scoping quirks that's not that hard.
|
| What I find way harder to cope with is the gazillions of type
| parameters with gazillions of guards, send, sync, clone or
| not clone, then impl Foo for Bar vs impl Foo for Arc<Bar> etc
| etc; and tons of features in cargo crates, also in private
| workspace projects, .... And the compiler slowness oh man the
| compiler slowness.
|
| EDIT: my life improved a bit since I learned about the cargo
| hakari plugin
| jph wrote:
| Some steps that newcomers ask me about involve memory
| management, the borrow checker, and sharing data, such as Rc,
| Arc, dyn, cell.
|
| https://doc.rust-lang.org/std/rc/struct.Rc.html
|
| https://doc.rust-lang.org/rust-by-example/std/arc.html
|
| https://doc.rust-lang.org/std/keyword.dyn.html
|
| https://doc.rust-lang.org/std/cell/
| duped wrote:
| Here's a case I ran into.
|
| I have a `Span` type and it can act like `RangeInclusive`. I
| want to index into a `&str` with it. This looks something like
| this : use std::ops::Index; pub
| struct Span { start:usize, end:usize,
| } impl<'a> Index<Span> for &'a str {
| type Output = str; fn index(&self, span:Span) ->
| &Self::Output { &self[span.start..=span.end]
| } }
|
| (NB4 RangeInclusive, in this example, Span is almost identical
| to RangeInclusive, in practice it was not)
|
| Playground: https://play.rust-
| lang.org/?version=stable&mode=debug&editio...
|
| This gives you a cryptic error :
| error[E0308]: mismatched types --> src/lib.rs:10:19
| | 10 | &self[span.start..=span.end]
| | ^^^^^^^^^^^^^^^^^^^^^ expected struct
| `Span`, found struct `RangeInclusive` | =
| note: expected struct `Span` found struct
| `RangeInclusive<usize>` For more information
| about this error, try `rustc --explain E0308
|
| This is weird, because you can definitely index into a `&str`
| with `RangeInclusive`.
|
| What you actually need to do is implement it for `str` instead
| of `&'a str`. This is a little peculiarity with how the `str`
| type works (to date, this is the only time I've ever needed the
| raw `str` type and not &str! it almost never is required) and
| the generic implementation for Index<I> for str combined with
| how Deref works.
|
| It's a small one line change to fix the code, but it's not
| obvious from the error message how to do it, and a bit tricky
| to solve unless you knew a little bit more about how Rust's
| operators get parsed/implemented.
|
| In my experience this is a common class of problems when you
| deal with generic and trait programming in Rust. It's
| ultimately the front end to an advanced constraint solver, and
| due to the explosion of complexity when you start making
| generic things more/less generic you will hit the wall with
| weirdness and need to understand more about the type system
| rather than letting the type system prevent you from doing
| stupid things.
|
| Frankly the memory management issue is not a problem once you
| buy into ownership.
| ridiculous_fish wrote:
| I sheepishly admit to repeatedly trying and failing to overcome
| modules.
| zcmack wrote:
| this was super effective at convincing me not to learn rust
| lumost wrote:
| The linked list debate should really be improved. From a
| practical perspective there are many reasons why one may wish to
| model a cyclic, linked datastructure.
|
| Simple examples:
|
| You have a social networking site that you are prototyping. You
| choose to start your data model with a simple Person struct which
| contains a list of Person called friends.
|
| You have an e-commerce site and choose a data model which has a
| User struct which contains a Cart reference, and a Cart struct
| containing a User reference for its owning user.
|
| You have any problem which involves a graph data-structure.
|
| In Rust the best way to work around these issues is to have
| explicit lookup's by id rather than direct traversable
| references, or use unsafe. There are no other mechanisms. It's a
| missing feature that really should be highlighted early in the
| docs or even as a compiler error such as "cyclic ownership
| detected in Struct X -> References Y -> References Z in
| main.rs#128 consider removing the cycle or using Rc, Arena, or
| unsafe allocations"
|
| As it stands if you create a reference cycle in rust you'll get a
| ton of compiler errors that force you to learn the rest of the
| ownership rules just to find out that this is something you can
| never do.
| nauticacom wrote:
| I don't enjoy writing Rust but its perspective on this is at
| least internally consistent. What are the alternatives for
| managing cyclic, linked data structures in other languages?
|
| In languages with manual memory management you can do it, but
| it's incredibly easy to mess up. You either have to maintain
| the entire mental model of who owns what data and what data has
| been initialized in your head or write it down and risk that
| becoming out of date.
|
| In languages with a GC, the implementors have assumed this
| complexity for you. Depending on what style of GC your language
| uses the exact strategy will be different, but on a GC is a
| comparatively complex piece of code, especially one which
| handles reference cycles well like we're talking about here.
|
| Either way, the complexity is there somewhere. If you manage
| memory yourself, you deal with the complexity yourself in a
| hard-to-debug way. If you can accept the tradeoffs of a GC,
| then the complexity is abstracted behind the GC. Because Rust
| is designed for use-cases where you can't accept the tradeoffs
| of a GC, it _has_ to surface that inherent complexity
| somewhere. It decides to surface it with ownership semantics,
| which at least make the rules you 're following in manual
| languages explicit and (largely) unbreakable.
|
| Yeah it's complex, but the underlying problem is complex. I can
| use GC languages, so I do, but you'd be no better off in C.
| dystroy wrote:
| What I meant in this article is that designing the ways to deal
| with graph-like structures should be done after the ownership
| model has been understood. If you start your first program
| directly on this kind of problem, you'll be only fighting the
| borrow checker and not really enjoying Rust.
|
| I should probably have taken my own experience with broot
| rather than the linked list example (which hurts many fans of
| its theoretical beauty).
| jph wrote:
| > trust the input of your fellow functions, and don't lose too
| much time looking for assertions to add.
|
| Rust makes it so easy to add assertions that it's well worth
| doing IMHO, and the assertions make it easy to detect corner
| cases such as with lengths, collections, comparisons, unexpected
| inputs, etc.
|
| Rust comes with a handful of assertions, and there are crates
| that provide many more, as well as runtime equivalents for
| function input validation. I wrote the "assertables" crate to
| help with these: https://github.com/sixarm/assertables-rust-crate
| dystroy wrote:
| IMO those assertions are more useful in tests (which Rust make
| easy) than to protect your functions from inconsistent inputs.
| duped wrote:
| Returning a `Result<>` for corner cases is so much better than
| random asserts because it forces callers to handle them.
|
| Personally I reject anything that looks like `assert(...)`
| found outside of tests and ask they be replaced with a proper
| error or the idiomatic way to express a fatal/unrecoverable
| error in your language. In Rust that's `unreachable!` and
| `panic!`, not `assert!`.
| trevyn wrote:
| I think an appropriate assert_eq! is more idiomatic than an
| equivalent panic!, since it automatically reports the
| offending value in the panic message.
___________________________________________________________________
(page generated 2021-12-16 23:02 UTC)