|
|
|
@ -4,3 +4,62 @@ The dot operator will perform a lot of magic to convert types. It will perform
|
|
|
|
|
auto-referencing, auto-dereferencing, and coercion until types match.
|
|
|
|
|
|
|
|
|
|
TODO: steal information from http://stackoverflow.com/questions/28519997/what-are-rusts-exact-auto-dereferencing-rules/28552082#28552082
|
|
|
|
|
|
|
|
|
|
Consider the following example of the dot operator at work.
|
|
|
|
|
```rust.ignore
|
|
|
|
|
fn do_stuff<T: Clone>(value: &T) {
|
|
|
|
|
let cloned = value.clone();
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
What type is `cloned`? First, the compiler checks if we can call by value.
|
|
|
|
|
The type of `value` is `&T`, and so the `clone` function has signature
|
|
|
|
|
`fn clone(&T) -> T`. We know that `T: Clone`, so the compiler finds that
|
|
|
|
|
`cloned: T`.
|
|
|
|
|
|
|
|
|
|
What would happen if the `T: Clone` restriction was removed? We 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 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 behaviour is used to create some subtle
|
|
|
|
|
effects.
|
|
|
|
|
```rust.ignore
|
|
|
|
|
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)
|
|
|
|
|
```rust.ignore
|
|
|
|
|
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`](https://doc.rust-lang.org/std/clone/trait.Clone.html#derivable),
|
|
|
|
|
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`.
|
|
|
|
|
```rust.ignore
|
|
|
|
|
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>`.
|
|
|
|
|