From d0a45b752c09cb61abac114a82c2028cf01068da Mon Sep 17 00:00:00 2001 From: Thirds <50671761+thirdsgames@users.noreply.github.com> Date: Fri, 16 Jul 2021 09:23:28 +0100 Subject: [PATCH] Apply suggestions from code review Co-authored-by: Yuki Okushi --- src/dot-operator.md | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/dot-operator.md b/src/dot-operator.md index 751eef6..a3c2701 100644 --- a/src/dot-operator.md +++ b/src/dot-operator.md @@ -26,8 +26,9 @@ This just means that if `T` has a size parameter known at compile time, we "forg 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. -```rust.ignore +Here is an example of the method lookup algorithm: + +```rust,ignore let array: Rc> = ...; let first_entry = array[0]; ``` @@ -38,21 +39,23 @@ trait - the compiler will convert `array[0]` into `array.index(0)`. Now, the compiler checks to see if `array` implements `Index`, so that we can call the function. -First, the compiler checks if `Rc>` implements `Index`, but it +Then, the compiler checks if `Rc>` implements `Index`, but it does not, and neither do `&Rc>` or `&mut Rc>`. Since none of these worked, the compiler dereferences the `Rc>` into -`Box<[T; 3]>` and tries again. `Box<[T; 3]>`, `&Box<[T; 3]>` and `&mut Box<[T; 3]>` +`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`. We can't dereference `[T; 3]`, so the compiler unsizes it, giving `[T]`. Finally, `[T]` implements `Index`, so we can now call the actual `index` function. -Consider the following more complicated example of the dot operator at work. -```rust.ignore +Consider the following more complicated example of the dot operator at work: + +```rust fn do_stuff(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 @@ -60,15 +63,16 @@ The type of `value` is `&T`, and so the `clone` function has signature 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 +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 behaviour is used to create some subtle -effects. -```rust.ignore -use std::sync::Arc; +Here is another example where the autoref behavior is used to create some subtle +effects: +```rust +# use std::sync::Arc; +# #[derive(Clone)] struct Container(Arc); @@ -77,11 +81,13 @@ fn clone_containers(foo: &Container, bar: &Container) { let bar_cloned = bar.clone(); } ``` + What types are `foo_cloned` and `bar_cloned`? We know that `Container: Clone`, so the compiler calls `clone` by value to give `foo_cloned: Container`. However, `bar_cloned` actually has type `&Container`. 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) +Looking closer, the code generated by the `derive` macro is (roughly): + ```rust.ignore impl Clone for Container where T: Clone { fn clone(&self) -> Self { @@ -89,6 +95,7 @@ impl Clone for Container where T: Clone { } } ``` + The derived `Clone` implementation is [only defined where `T: Clone`][clone], so there is no implementation for `Container: Clone` for a generic `T`. The @@ -96,17 +103,19 @@ compiler then looks to see if `&Container` implements `Clone`, which it does. So it deduces that `clone` is called by autoref, and so `bar_cloned` has type `&Container`. -We can fix this by implementing `Clone` manually without requiring `T: Clone`. -```rust.ignore +We can fix this by implementing `Clone` manually without requiring `T: Clone`: + +```rust,ignore impl Clone for Container { fn clone(&self) -> Self { Self(Arc::clone(&self.0)) } } ``` + Now, the type checker deduces that `bar_cloned: Container`. -[fqs]: https://doc.rust-lang.org/nightly/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name +[fqs]: ../book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name [method_lookup]: https://rustc-dev-guide.rust-lang.org/method-lookup.html -[index]: https://doc.rust-lang.org/std/ops/trait.Index.html -[clone]: https://doc.rust-lang.org/std/clone/trait.Clone.html#derivable +[index]: ../std/ops/trait.Index.html +[clone]: ../std/clone/trait.Clone.html#derivable