Created
June 1, 2022 15:12
-
-
Save Oyelowo/dfbc5872d4c2eafc66847772d241544d to your computer and use it in GitHub Desktop.
Blogpost1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The nuances of Rust's `impl Trait` syntactic sugar | |
A brief discussion in a discord channel about language design is the motivation behind this writing. | |
What happens when you have gthe function below: | |
```rs | |
trait Speed { } | |
fn get_oyelowo(arg1: impl Speed , arg2: impl Speed) { | |
let a = arg1; | |
let b = arg2; | |
a = arg2; // This gives an error | |
} | |
``` | |
Rust would give the error below, even though they implement thesame trait. | |
```rs | |
mismatched types | |
expected type parameter `impl Speed` (type parameter `impl Speed`) | |
found type parameter `impl Speed` (type parameter `impl Speed`) | |
a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound | |
``` | |
but when you try this, it works: | |
```rs | |
fn get_oyelowo_generics<T: Speed>(arg1: T , arg2: T) { | |
let a = arg1; | |
let b = arg2; | |
a = arg2; | |
} | |
``` | |
So, why is that? | |
#### ANSWER | |
Actually, when you use that syntactic sugar - `impl Trait`, Rust, essentially creates | |
separate generics for all the arguments because, despite the arguments implementing | |
same trait, they might actually have different concrete types when the function is called. | |
Which means function 1 is actually "desugared" to function 2 rather than function 3: | |
```rs | |
trait Speed { } | |
// FUNCTION 1 | |
fn get_oyelowo_variation1(arg1: impl Speed , arg2: impl Speed) { | |
let a = arg1; | |
let b = arg2; | |
a = arg2; | |
} | |
// FUNCTION 2 | |
// This is the right one - the arguments are separate - and | |
// can have different concrete types | |
fn get_oyelowo_variation2<A: Speed, B: Speed>(arg1: A , arg2: B) { | |
let a = arg1; | |
let b = arg2; | |
a = arg2; | |
} | |
// FUNCTION 3 | |
// This is not the desugared version because it ties the arguments | |
// to thesame concrete types | |
fn get_oyelowo_variation3<A: Speed>(arg1: A , arg2: A) { | |
let a = arg1; | |
let b = arg2; | |
a = arg2; | |
} | |
``` | |
### Example | |
Say, we have various structs that implement the trait e.g | |
```rs | |
trait SpaceTrait { } | |
struct SpaceHuman { | |
id: Uuid, | |
base: String, | |
} | |
impl SpaceTrait for SpaceHuman { | |
} | |
struct SpaceDog { | |
id: Uuid, | |
breed: DogBreed, | |
} | |
impl SpaceTrait for SpaceDog { | |
} | |
``` | |
In this case, let's call the function on the above concrete types. | |
If the above were to be desugared to thesame concrete type, we would not be | |
able to compare the two creatures, even though they both implement thesame trait | |
```rs | |
// The function: | |
fn compare_attributes(creature1: impl SpaceTrait, creature: impl SpaceTrait) { | |
} | |
// INCORRECT DISUGARED VERSION | |
fn compare_attributes<T: SpaceTrait>(creature1: T, creature: T) { | |
} | |
let human = SpaceHuman {..}; | |
let dog = SpaceDog {..}; | |
let result = compare_attributes(dog , human); // This will fail because they are of separate concrete types. | |
``` | |
The above will fail because dog is not human and vice versa, despite them sharing traits. | |
Therefore, it makes sense that Rust would err on the side of making them have different concrete types. | |
i.e | |
```rs | |
fn compare_attributes(creature1: impl SpaceTrait, creature: impl SpaceTrait) { | |
} | |
// CORRECT DISUGARED VERSION. | |
// notice the different aarguments? | |
fn compare_attributes<T: SpaceTrait, U: SpaceTrait>(creature1: T, creature: U) { | |
} | |
// can also be written as: | |
fn compare_attributes<T: SpaceTrait, U: SpaceTrait>(creature1: T, creature: U) | |
where | |
T: SpaceTrait, | |
U: SpaceTrait | |
{ | |
} | |
let human = SpaceHuman {..}; | |
let dog = SpaceDog {..}; | |
let result = compare_attributes(dog , human); // This will now work. | |
``` |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment