1. 63

  2. 7

    Man, my least favorite Rust type is closures. They’re magical enough that they Just Do What You Want 98% of the time, and the other 2% there’s some weird borrowing issue, or the type inference gets confused, or Something Else Happens that one way or another makes them not actually interchangeable with functions.

    It’s interesting to highlight warts in Rust’s stdlib though, since a lot of work went into making it (relatively) wart-free despite doing lots of complicated stuff. Other favorites of mine: Box is slightly magical and doesn’t need to be (but did once), Path and PathBuf have never ever made me happy ever, and SocketAddr and everything related is just a mess.

    1. 1

      I seem to have avoided that 2%. What sucks about Rust’s implementation of closures?

      1. 2

        Each closure is its own type, and they are not interchangable. Plus, for a newbie, how they interact with lifetimes is a bit arcane. Coming from OCaml, this tripped me up many times when I did things like try to store a closure in a struct, have functions that returned different closures to define different behaviors, use them for partial evaluation, etc. You CAN do all of this, and the story has improved a lot with various features Rust has added over time, but it was a nasty shock.

    2. 5

      This reminds me of the year-long issues Scala had with its range type(s) (which also tried to do way too much).


      Range::is_empty() could be written naturally instead of a a bizarre way just for NaNs

      seems to be more of an issue with Rust’s not-that-good Eq/Ord hierarchies that they lifted from Haskell (where it was already an issue).

      1. 2

        Can you give me a pointer to the problems of the Eq/Ord hierarachy in Haskell?

        1. 5

          You basically have two different notions: A machine-level one based on identity of bit patterns, and one of a user-defined, domain-specific equality.

          In most cases, it doesn’t matter because they are one and the same – e. g. for integers

          But for things like floating point numbers, it really hurts. E. g. in languages like Rust and Haskell, you can’t check whether some data structure contains a NaN, or whether it really has 0.0, not the “sufficiently similar” -0.0.

          This is because they put “partial” and “total” operations into a hierarchy, which means that if your “total operation” is not behaviorally a sub type of the “partial” operation, language designers decided that you simply don’t get to use the total one at all.

          The problem is solved fairly easy though – simply use different types for different things.

          I went with Equals/Identity and Comparable/Sortable (because “partial” vs. “total” is really not that interesting in describing the operations), and it works so exceedingly well that it’s painful to watch other languages getting it wrong.

      2. 2

        Wow, ridiculousfish posting rust.

        But I wish to see Range can work backwards, .rev() make that a different type and hard to compare.

        1. 1

          Good to see the fish posting again.