Apply suggestions from code review

Co-authored-by: Yuki Okushi <jtitor@2k36.org>
pull/292/head
Thirds 4 years ago committed by GitHub
parent f4653e1234
commit d0a45b752c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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 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. convert `[i32; 2]` into `[i32]` by "forgetting" the size of the array.
Here is an example of the method lookup algorithm. Here is an example of the method lookup algorithm:
```rust.ignore
```rust,ignore
let array: Rc<Box<[T; 3]>> = ...; let array: Rc<Box<[T; 3]>> = ...;
let first_entry = array[0]; 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 compiler checks to see if `array` implements `Index`, so that we can call the
function. function.
First, the compiler checks if `Rc<Box<[T; 3]>>` implements `Index`, but it 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 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 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]>` `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 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 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 unsizes it, giving `[T]`. Finally, `[T]` implements `Index`, so we can now call the
actual `index` function. actual `index` function.
Consider the following more complicated example of the dot operator at work. Consider the following more complicated example of the dot operator at work:
```rust.ignore
```rust
fn do_stuff<T: Clone>(value: &T) { fn do_stuff<T: Clone>(value: &T) {
let cloned = value.clone(); let cloned = value.clone();
} }
``` ```
What type is `cloned`? First, the compiler checks if we can call by value. 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 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 `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 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 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 `fn clone(&&T) -> &T` since `Self = &T`. The compiler sees that `&T: Clone`, and
then deduces that `cloned: &T`. then deduces that `cloned: &T`.
Here is another example where the autoref behaviour is used to create some subtle Here is another example where the autoref behavior is used to create some subtle
effects. effects:
```rust.ignore
use std::sync::Arc;
```rust
# use std::sync::Arc;
#
#[derive(Clone)] #[derive(Clone)]
struct Container<T>(Arc<T>); struct Container<T>(Arc<T>);
@ -77,11 +81,13 @@ fn clone_containers<T>(foo: &Container<i32>, bar: &Container<T>) {
let bar_cloned = bar.clone(); let bar_cloned = bar.clone();
} }
``` ```
What types are `foo_cloned` and `bar_cloned`? We know that `Container<i32>: 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>`. 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 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`! 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 ```rust.ignore
impl<T> Clone for Container<T> where T: Clone { impl<T> Clone for Container<T> where T: Clone {
fn clone(&self) -> Self { fn clone(&self) -> Self {
@ -89,6 +95,7 @@ impl<T> Clone for Container<T> where T: Clone {
} }
} }
``` ```
The derived `Clone` implementation is The derived `Clone` implementation is
[only defined where `T: Clone`][clone], [only defined where `T: Clone`][clone],
so there is no implementation for `Container<T>: Clone` for a generic `T`. The so there is no implementation for `Container<T>: Clone` for a generic `T`. The
@ -96,17 +103,19 @@ 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 So it deduces that `clone` is called by autoref, and so `bar_cloned` has type
`&Container<T>`. `&Container<T>`.
We can fix this by implementing `Clone` manually without requiring `T: Clone`. We can fix this by implementing `Clone` manually without requiring `T: Clone`:
```rust.ignore
```rust,ignore
impl<T> Clone for Container<T> { impl<T> Clone for Container<T> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self(Arc::clone(&self.0)) Self(Arc::clone(&self.0))
} }
} }
``` ```
Now, the type checker deduces that `bar_cloned: Container<T>`. Now, the type checker deduces that `bar_cloned: Container<T>`.
[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 [method_lookup]: https://rustc-dev-guide.rust-lang.org/method-lookup.html
[index]: https://doc.rust-lang.org/std/ops/trait.Index.html [index]: ../std/ops/trait.Index.html
[clone]: https://doc.rust-lang.org/std/clone/trait.Clone.html#derivable [clone]: ../std/clone/trait.Clone.html#derivable

Loading…
Cancel
Save