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 Trait
bindings 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 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
- Especially when interacting with
- 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
- 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,
async
issue - 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
Unsize
a dynamically sized type, so you can't make adyn Trait
out 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: Sized
as 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 Trait
is 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 Trait
that 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
ControlFlow
parameter 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
stdlib
types 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 (theAssoc
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
- Match ergonomics / match binding modes
- Is broken
- (other complaints to be expanded later)
- Fallback integer is
i32
but 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
default
fordefault impl
because this is "less confusing" than calling thempartial impl
- Makes
default
implicit 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
bstr
crate 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 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