The Dot Operator - The Rustonomicon
Description: The Dark Arts of Advanced and Unsafe Rust Programming

## Keyboard shortcuts

Press `←` or `→` to navigate between chapters

Press `S` or `/` to search in the book

Press `?` to show this help

Press `Esc` to hide this help

# The Dot Operator

The dot operator will perform a lot of magic to convert types. It will perform auto-referencing, auto-dereferencing, and coercion until types match. The detailed mechanics of method lookup are defined here, but here is a brief overview that outlines the main steps.

Suppose we have a function `foo` that has a receiver (a `self`, `&self` or `&mut self` parameter). If we call `value.foo()`, the compiler needs to determine what type `Self` is before it can call the correct implementation of the function. For this example, we will say that `value` has type `T`.

We will use fully-qualified syntax to be more clear about exactly which type we are calling a function on.

- First, the compiler checks if it can call `T::foo(value)` directly. This is called a “by value” method call.
- If it can’t call this function (for example, if the function has the wrong type or a trait isn’t implemented for `Self`), then the compiler tries to add in an automatic reference. This means that the compiler tries `<&T>::foo(value)` and `<&mut T>::foo(value)`. This is called an “autoref” method call.
- If none of these candidates worked, it dereferences `T` and tries again. This uses the `Deref` trait - if `T: Deref<Target = U>` then it tries again with type `U` instead of `T`. If it can’t dereference `T`, it can also try unsizing `T`. This just means that if `T` has a size parameter known at compile time, it “forgets” it for the purpose of resolving methods. For instance, this unsizing step can convert `[i32; 2]` into `[i32]` by “forgetting” the size of the array.

Here is an example of the method lookup algorithm:

    let array: Rc<Box<[T; 3]>> = ...;
    let first_entry = array[0];

How does the compiler actually compute `array[0]` when the array is behind so many indirections? First, `array[0]` is really just syntax sugar for the `Index` trait - the compiler will convert `array[0]` into `array.index(0)`. Now, the compiler checks to see if `array` implements `Index`, so that it can call the function.

Then, the compiler checks if `Rc<Box<[T; 3]>>` implements `Index`, but it does not, and neither do `&Rc<Box<[T; 3]>>` or `&mut Rc<Box<[T; 3]>>`. Since none of these worked, the compiler dereferences the `Rc<Box<[T; 3]>>` into `Box<[T; 3]>` and tries again. `Box<[T; 3]>`, `&Box<[T; 3]>`, and `&mut Box<[T; 3]>` do not implement `Index`, so it dereferences again. `[T; 3]` and its autorefs also do not implement `Index`. It can’t dereference `[T; 3]`, so the compiler unsizes it, giving `[T]`. Finally, `[T]` implements `Index`, so it can now call the actual `index` function.

Consider the following more complicated example of the dot operator at work:

    #![allow(unused)]
    fn main() {
    fn do_stuff<T: Clone>(value: &T) {
        let cloned = value.clone();
    }
    }

What type is `cloned`? First, the compiler checks if it can call by value. The type of `value` is `&T`, and so the `clone` function has signature `fn clone(&T) -> T`. It knows that `T: Clone`, so the compiler finds that `cloned: T`.

What would happen if the `T: Clone` restriction was removed? It would not be able to call by value, since there is no implementation of `Clone` for `T`. So the compiler tries to call by autoref. In this case, the function has the signature `fn clone(&&T) -> &T` since `Self = &T`. The compiler sees that `&T: Clone`, and then deduces that `cloned: &T`.

Here is another example where the autoref behavior is used to create some subtle effects:

    #![allow(unused)]
    fn main() {
    use std::sync::Arc;

    #[derive(Clone)]
    struct Container<T>(Arc<T>);

    fn clone_containers<T>(foo: &Container<i32>, bar: &Container<T>) {
        let foo_cloned = foo.clone();
        let bar_cloned = bar.clone();
    }
    }

What types are `foo_cloned` and `bar_cloned`? We know that `Container<i32>: Clone`, so the compiler calls `clone` by value to give `foo_cloned: Container<i32>`. However, `bar_cloned` actually has type `&Container<T>`. Surely this doesn’t make sense - we added `#[derive(Clone)]` to `Container`, so it must implement `Clone`! Looking closer, the code generated by the `derive` macro is (roughly):

    impl<T> Clone for Container<T> where T: Clone {
        fn clone(&self) -> Self {
            Self(Arc::clone(&self.0))
        }
    }

The derived `Clone` implementation is only defined where `T: Clone`, so there is no implementation for `Container<T>: Clone` for a generic `T`. The compiler then looks to see if `&Container<T>` implements `Clone`, which it does. So it deduces that `clone` is called by autoref, and so `bar_cloned` has type `&Container<T>`.

We can fix this by implementing `Clone` manually without requiring `T: Clone`:

    impl<T> Clone for Container<T> {
        fn clone(&self) -> Self {
            Self(Arc::clone(&self.0))
        }
    }

Now, the type checker deduces that `bar_cloned: Container<T>`.