Gotchas and warts in Rust Lang (IMHO).
- 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.impl Traitbindings could help but are currently dis-implemented- Generalized type ascription would have helped but has been postponed.
- 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 Traitlifetime 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
- Especially when interacting with
- The elision rules sometimes choose the wrong lifetime
- The elision rules naturally do not penetrate aliases... including
Selfor 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
- Argument Position
impl Trait(APIT)- Some love it, some hate it. Distinguishing from RPIT is definitely a stumbling block to beginners
- Part of RFC 1951
- Outlook: Divisive but stable and unlikely to change
impl Trait"leaks" auto-traits- Probably only a gotcha in that it's easy to break backwards compatibility (so far)
- Outlook: Stable, by design, and probably can't change
- The "captures" gotcha
- Described here and explored here
- See also: Crate work-around,
asyncissue - Outlook: Something like
dyn Trait's lifetime intersection is desired but has not yet materialized. Rust teams unwilling to stabilize a work-around in the meanwhile.
- You can't
Unsizea dynamically sized type, so you can't make adyn Traitout of a[T],str, etc.- Indirection (
Box<str>,&String, ...) is sometimes an option instead
- Indirection (
- 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
- The lifetime is covariant and that covariance applies during unsized coercion, which can bypass otherwise invariant contexts such as inside an
where Self: Sizedas a coarse approximation forwhere Self: !Dyn- Precludes implementing a function not only for
dyn ThisTrait, butdyn 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
- Precludes implementing a function not only for
- Inherent methods "are" the trait implementation
- Striving too hard for
dyn Trait"is" the trait vs. the reality:dyn Traitis a distinct, concrete type?- No, apparently it was due to partial (compiler) implementations
- I still feel inherent impls should be inherent impls and trait impls, trait impls.
- Related: The belief/desire for it to be impossible to make a
dyn Traitthat doesn't implementTrait- See also RFC 2027
- Outlook: Stable, but could probably just be allowed?
- Striving too hard for
- 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 parameters are just generally a half baked feature
- Doesn't interact with inference
- RFC 213 basically cancelled
- Underdocumented
- Outlook: Could improve but no momentum to do so
ControlFlowparameter order- The order was changed and a default added back when the primary use was iterator combinators
- Optimizing for writing via the default parameter
- The MCP to expand its usage happened afterwards
- That includes the
Try/?operator
- That includes the
- Now it's confusing that the order is the opposite of
Result - Outlook: Stabilized and we're stuck with it forever
- The order was changed and a default added back when the primary use was iterator combinators
_does not bind- So
let _ = ...is different fromlet _name = ... - Which often makes whatever it matches into a temporary
- Outlook: Permanent. It supplies unique abilities in some cases.
- So
- Drop glue determines owned lifetime (drop at end of scope)
- Despite NLL, which can be surprising
- Especially in the face of
stdlibtypes utilizing RFC 1327 (#[may_dangle]) - Outlook: Probably permenant?
#[may_dangle]stabilization would help some
- NLL problem case #3
- Outlook: Waiting on Polonius
- Plain and generic type aliases lack the rigor of associated types and TAIT
- Trait bounds are not enforced, nor lifetime bounds
- See also
- Outlook: See here, presumably something that will be improved as TAIT comes to be? I hope so anyway.
- There's no way to specify which trait an associated type came from in
Trait<Assoc = Type>form (theAssoccan 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
- Match ergonomics / match binding modes
- Is broken
- (other complaints to be expanded later)
- Fallback integer is
i32but fallback float isf64- 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
- Mandatory in
- Partial implementations as implemented on nightly (part of Specialization)
- Recycles the name
defaultfordefault implbecause this is "less confusing" than calling thempartial impl - Makes
defaultimplicit on the implementations within - Thus they are not finalizable
- Outlook: Not stable and considered an unresolved question
- Recycles the name
- Lack of Needle API (RFC 2500)
- There is no replacement other than the
bstrcrate for[u8] - RFC 2295 may help with
OsString - Outlook: could be revamped but momentum is lacking
- There is no replacement other than the
Closures capture entire variables, not fieldsOutlook: RFC 2229 will stabilize in edition 2021- Is stable 🚀
- ✨ Self referential structs ✨
- Inference and coercion order
- Here's an example of inference and coercion interacting for a surprising result.
dyn Traitgenerally&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