Auto-dereferencing in Rust: how it works and why it's not magic
\n\nIf you've ever written in Rust, you've probably noticed that the compiler allows you to call methods on references and smart pointers as if you were working with the value itself. This is not magic and not a bug — it's a strictly defined mechanism called auto-dereferencing.
\n\nOn Stack Overflow, the question «What are Rust's exact auto-dereferencing rules?» has gathered thousands of views — and for good reason. Understanding these rules turns Rust code from a «set of strange errors» into an elegant and predictable tool. Let's look under the hood.
\n\nWhat is auto-dereferencing and why is it needed?
\n\nIn Rust, we have references (&T, &mut T) and smart pointers (Box<T>, Rc<T>, Arc<T>). When you write my_box.some_method(), the compiler automatically applies dereferencing to reach the inner value T. Without this mechanism, you would have to write (*my_box).some_method() or (**my_ref).some_method() — which quickly becomes tedious and clutters the code.
Auto-dereferencing makes code clean, but it's important to understand its boundaries and exact rules to avoid pitfalls.
\n\nExact rules of auto-dereferencing in Rust
\n\nLet's break down the official specification. When the compiler encounters an expression like x.method(), it tries to find a method named method for type T, where T is the type of x. If the method is not found, the compiler begins the process of automatic dereferencing (or taking a reference).
Here is the sequence of steps the compiler performs (simplified):
\n\n- \n
- Step 1: Try to find a method for type
T(as is). \n - Step 2: If not found — try to find a method for
&Tor&mut T(taking a reference). \n - Step 3: If not found — apply
Deref, i.e., dereference once (getUfromT). \n - Step 4: Repeat steps 1-3 for the new type
U. \n - Step 5: If after several dereferences the method is still not found — compilation error. \n
Important: the compiler does not create an infinite chain. It applies dereferencing until it finds a method or hits a type that does not implement Deref.
Examples: from simple to complex
\n\nLet's look at code that demonstrates these rules in action.
\n\nExample 1: Basic reference
\n\nstruct Point {\n x: i32,\n y: i32,\n}\n\nimpl Point {\n fn distance_from_origin(&self) -> f64 {\n ((self.x.pow(2) + self.y.pow(2)) as f64).sqrt()\n }\n}\n\nfn main() {\n let p = Point { x: 3, y: 4 };\n let r = &p; // &Point\n println!("{}", r.distance_from_origin()); // 5.0\n}\n\nHere r is &Point. The method distance_from_origin takes &self. The compiler sees: r is &Point. It tries to find a method for &Point — it finds it! Everything works without explicit dereferencing.
Example 2: Double reference
\n\nfn main() {\n let p = Point { x: 3, y: 4 };\n let r1 = &p; // &Point\n let r2 = &r1; // &&Point\n println!("{}", r2.distance_from_origin()); // 5.0\n}\n\nThe type of r2 is &&Point. The compiler tries to find a method for &&Point — no. It tries to take a reference: &&&Point — also no. Then it applies Deref once: it gets &Point. For
Blogs
Book Recommendations
Read also
The Secret of [Flags] in C#: How to Write Code Even Juniors Will Understand
Generics in Swift: Why They Are Needed and How to Use Them
Stack or Heap in Go: How Struct Allocation Affects GC
Property has no initializer in TypeScript: how to fix in 5 minutes
Monitoring Swift Applications: Advanced Metrics and Alerts