/rust-snags

Gotchas and warts in Rust Lang

Creative Commons Zero v1.0 UniversalCC0-1.0

Rust Snags

Gotchas and warts in Rust Lang (IMHO).

Lifetime elision

  • Closures use different rules than functions
    • This makes creating higher-ranked closures arduous
    • So much so that many think it's impossible to make closures that are generic over liftimes
    • Outlook: Aparently backwards incompatible to change? Not sure why though. Could be largely fixed with some new syntax, e.g. the ability to ascribe an impl Fn(&Foo) type.
  • Struct lifetime parameter elision
    • By which I mean eliding the parameter altogether. Most seem to agree this is non-obvious and bad style.
    • Part of RFC 0141
    • Can also be a gotcha as an associated type ('static) vs return position (can default to input lifetime)
    • Will likely be a gotcha when defining a GAT
    • Alternative: Use the anonymous lifetime (<'_>, RFC 2115)
    • Outlook: Stable, but could be deprecated over time
  • dyn Trait lifetime elision
    • RFC 0599, RFC 1156, RFC 1214
    • More of a gotcha than a wart; not a problem if your lifetime of applicability is 'static
    • The elision rules are rather special-cased
      • Especially when interacting with '_ wildcard elision
    • The elision rules sometimes choose the wrong lifetime
    • The elision rules naturally do not penetrate aliases... including Self or associated types
    • In generic struct declarations it's based on the underlying type constraints
      • Which enforces incorrect impression that the lifetime is that of the underlying type
      • Probably too obscure to really be a snag people actually hit
    • Outlook: Stable and probably not changeable
  • "In-band" lifetimes
    • Seems to be off the table for now. Whew!
    • Outlook: "Retired" but may be revisited
    • Previous notes:
      • I.e. just using a named lifetime declares it (if not already declared)
      • Part of RFC 2115
      • Optimizes for writing, pessimizes reading, foot-gun

impl Trait

dyn Trait

  • You can't Unsize a dynamically sized type, so you can't make a dyn Trait out of a [T], str, etc.
    • Indirection (Box<str>, &String, ...) is sometimes an option instead
  • Lifetime variance and unsized coercion interaction
    • The lifetime is covariant and that covariance applies during unsized coercion, which can bypass otherwise invariant contexts such as inside an UnsafeCell
    • Surprising but I'm not sure if it's even a gotcha to be honest. Obscure.
    • Outlook: stable and unlikely to change
  • where Self: Sized as a coarse approximation for where Self: !Dyn
    • Precludes implementing a function not only for dyn ThisTrait, but dyn AnyTrait, str, [T], ...
    • Leads to situation where the only implementation for unsized types are default function bodies (need to re-find the citation for this situation)
    • Seems to be special-cased in the compiler (citation needed)
    • Outlook: Used endemically but a replacement should be possible
      • Technically possible after RFC 2580 ladnds
  • Inherent methods "are" the trait implementation
    • Striving too hard for dyn Trait "is" the trait vs. the reality: dyn Trait is a distinct, concrete type?
    • Related: The belief/desire for it to be impossible to make a dyn Trait that doesn't implement Trait
    • Outlook: Stable, but could probably just be allowed?

Variance

  • Traits are invariant
    • This is contrary to the RFC and was yanked with no in-band discussion really
    • Leads to somewhat painful variance-like implementations or helper methods
    • But arguably mostly a documentation / RFC follow-up issue
    • Outlook: Could be brought back but not on the horizon
  • GATs are invariant
    • Leads to somewhat painful variance-like implementations or helper methods
    • Outlook: GATs are not stable yet. Close though. Otherwise, same as for traits?

Default type paramaters

Ownership

Type aliases / Associated Types

  • Plain and generic type aliases lack the rigor of associated types and TAIT
  • There's no way to specify which trait an associated type came from in Trait<Assoc = Type> form (the Assoc can only be an identifier)
    • As discussed here
    • This can be problematic as dyn Trait<Assoc=X, Assoc=Y> cannot compile
    • Outlook: Rare problem, work-around possible, low priority

Misc

  • Match ergonomics / match binding modes
    • Is broken
    • (other complaints to be expanded later)
  • Fallback integer is i32 but fallback float is f64
    • Outlook: Permanent
  • Eliding parameter names is...
    • Mandatory in Fn(u32) and the like
    • Optional in fn(u32)/fn(x: u32) and the like
    • Not possible (in edition 2018+) in fn foo(x: u32) and the like
      • Do note these are patterns, not identifiers
  • Partial implementations as implemented on nightly (part of Specialization)
    • Recycles the name default for default impl because this is "less confusing" than calling them partial impl
    • Makes default implicit on the implementations within
    • Thus they are not finalizable
    • Outlook: Not stable and considered an unresolved question
  • Lack of Needle API (RFC 2500)
    • There is no replacement other than the bstr crate for [u8]
    • RFC 2295 may help with OsString
    • Outlook: could be revamped but momentum is lacking
  • Closures capture entire variables, not fields
    • Outlook: RFC 2229 will stabilize in edition 2021
    • Is stable 🚀
  • ✨ Self referential structs ✨

Mostly just lacking in documentation

  • Inference and coercion order
  • dyn Trait generally
  • &mut -> & function semantics
  • How functions capture more generally (e.g. complicated borrowing parameters)
    • By which I mean, returning something borrowed extends all (applicable?) input borrows
  • How the . operator works
    • Including field accesses, not just method resolution