Even tho I am not in favor of this particular proposal, it is not true that Rust forces everything to be explicit (c.f. type inference, default match bindings, etc.)
@H2CO3 did a great job explaining why the specific implicit things that exist in Rust are OK. Here is my clarification:
They are trivial to reason about. They do not have implications for performance or correctness or semantics.
Rust is a RAII language. It is expected that resources are managed through destructors (Drop
), cleaned up automatically at the end of the scope. This is consistent behaviour with no exceptions. It is trivial to reason about (you know whatever is owned in your current scope will be destroyed at the end of the scope).
Type inference: again, trivial to reason about. If the compiler can easily infer the type from the context, so can you. The type of everything is obvious from the immediate context around it. This is precisely why inference is a good thing: having to type out obvious information is unnecessary clutter. It does not affect semantics or performance or anything else of importance in any way.
Copy
, yes, while it does correspond to a runtime operation that is done implicitly, Rust guarantees that it will always be a single, simple memcpy
. This means that it is incredibly cheap to do (modern CPUs are very fast at memcpy
) and, again, trivial to reason about.
Deref
and friends: they are specifically intended only for cheap and trivial, obvious conversions. Same things apply.
Same thing with autoreferencing for match
.
Contrast all of this with proposals like implicit ?
or implicit await
. Both of these hide important information.
Implicit ?
silently converts a type to another one. This is dangerous. It also means that it is no longer obvious what operations can fail. You have to know the signature of the function you are calling, which often requires looking up documentation. This is not trivial to reason about. It also introduces the possibility of an early return, which means that subsequent code will not execute if a fallible operation fails, and this not being obvious from the code is a very bad thing that can lead to unexpected surprises.
Implicit await
hides the semantics of async
functions. You cannot know how a given code calling a given function will behave, without consulting the signature of the called function. It makes reasoning about code non-trivial.
This is really my big objection. Any implicit thing in the language should only be something that is truly obvious from looking only at the immediate context where it is written, and trivial to reason about.
Please tone down the all-caps, etc.
Done . I feel strongly about this issue and my previous post was maybe a little too-ranty.
@rpjohnst
We already have quite a few “implicit” things that affect control flow (panics, Drop, Deref), data types (inference, coercions, default binding modes), and potentially-expensive operations (Copy, Try conversions, also Deref).
You even call some of these out, but you don’t really justify why they’re okay while try/async would not be. There’s been quite extensive discussion here, more blanket “DO NOT” declarations do not really help advance that discussion.
Done. Addressed and clarified my views in detail. Hopefully this new post of mine advances the discussion in a more productive way .