{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreihttf5zremca3vf7wkqu7qicbqobxox3zevlzkvbxwusryar2vjfm",
"uri": "at://did:plc:ivbknywyskln22er3nkssdhl/app.bsky.feed.post/3mfpwjeln3y32"
},
"path": "/t/method-auto-de-ref-and-lack-of-it-in-rfc-132-ufcs/24005#post_8",
"publishedAt": "2026-02-25T23:27:47.000Z",
"site": "https://internals.rust-lang.org",
"tags": [
"[1]",
"1]. It's the case today that `x` as a value expression is _always_ a move[[2]",
"[3]",
"de]ref, but extracted out of method syntax, autoref is just adding a correctly-mutable reference[[4]",
"↩︎",
"[playground]",
"playground] [↩︎"
],
"textContent": "If `T::method(x, ...)` can mutate `x` (autoref to `&mut T`), that's a design issue in my opinion1]. It's the case today that `x` as a value expression is _always_ a move[[2]. Technically `x` is a place expression that gets coerced to a value in argument position, but it would very confusing to make an existing value expression place treat place expressions differently. In method syntax `x.method(...)`, `x` is treated as a place expression.\n\nThus the only version of autoref on argument position I could support would autoref not to `&[mut] $expr` semantics but instead to `&[mut] { $expr }` semantics, which puts the expression still in a value expression position, meaning that the source place gets moved from.\n\nAdditionally, in Rust as its designed today, methods aren't special; they're just standard functions that can be called just like any other associated function. It's method call syntax which has the special behavior. That's why I even support not just method disambiguation but full unified _method_ call syntax, allowing any function value to be invoked with method call semantics, leaning into the method call syntax being what's special, not the method item. Autoref for UMCS receivers would make method items into special function items again in more than just availability to method name lookup.\n\nHowever, I definitely have felt the desire for the ability to apply method-style autoref in macro expansions, where you typically want to call fully qualified names to ensure that the macro is not dependent on the namespace context that it's invoked in. This is probably a _very_ niche use case, but I'd even appreciate the ability to do `match k#autoref $x { x => { /* ... */ }}` so that `m!(x)` only evaluates the expression argument a single time with the caller's lifetime extension context but can still apply autoref if applicable.[3]\n\nI could, on the other hand, potentially support automatically _reborrowing_ non-reference argument places. `&mut` argument places are special in that they're always passed as `&[mut] *x`, meaning that the binding `x` remains initialized after, even bypassing the mutability of the binding. This doesn't occur for other `impl Deref`; e.g. you can't do `f(x)` where `x: Box<i32>, f: fn(&i32)`, you need to introduce the reference yourself as `f(&x)` in order for autoderef to project through. I remember the change that mostly eliminated the need to ever write `&*`. I didn't fully understand exactly how that works back then, but I think I now understand that it enabled autoderef to project into custom `impl Deref` _if autoderef is already started_ (i.e. by a top-level primitive reference). I think nowadays we could justify removing this restriction, thus allowing automatic reborrows of non-reference types, at least non-mutably.\n\nTo make sure people understand my term usage, my mental model is, given place `p`:\n\n * autoref: `&[mut] p`\n * autoderef: `&[mut] **p`, potentially recursively\n * reborrow: `&[mut] *p`\n\n\n\nIn practice method name lookup is fundamentally intertwined with autode]ref, but extracted out of method syntax, autoref is just adding a correctly-mutable reference[[4] that then can get autoderef'd to the correct pointee type.\n\n* * *\n\n 1. In the time up to and around 1.0, avoiding the concept of argument passing modes that C++ has was a much more prominent feature of Rust's design, so that potentially explains why RFC 132 doesn't feel the need to mention autoref. Autoref is what allows `&self` methods to be selected by method syntax, \"obviously\" it wouldn't apply to _function_ call syntax. I'd even hazard a guess that the ability for method syntax to select `<&T>::foo` on a receiver of type `T` was unintentional; you couldn't impl for `&T` outside std until RFC 1023 was implemented. ↩︎\n\n 2. Yes, even when `T: Copy`. The _only_ difference is that moving from a `Copy` value does not invalidate the source place. Well... except for the special case for reborrowing `&mut`. ↩︎\n\n 3. Yes, this is the _exact_ semantics that I don't support when applying autoref to qualified method UFCS. ↩︎\n\n 4. Obviously only if the target type is a reference. Notably, it does _not_ require the `self` argument to be a reference; `x.foo()` can resolve to `<&T>::foo(self)`. `&_` has no inherent methods, so this has to be a trait method, and `T::foo` would be selected before `<&T>::foo` if there are any _potentially_ valid trait impls for `T`. playground] [↩︎\n\n\n",
"title": "Method auto-(de)ref (and lack of it) in RFC 132 ufcs"
}