1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-04-26 17:23:01 +02:00
comprehensive-rust/src/generics/generic-functions.md
Nicole L 7f45460811
Rework generic fn examples to show monomorphized versions (#2671)
Something I always do when covering generic fns is I like to show the
monomorphized versions of `pick` to make it clear to students what
generics are doing behind the scenes. In my most recent class I tried
going the other way around, showing the monomorphized versions first to
more clearly motivate what generics are used for, and I liked the way it
went. I think motivating generics by first showing code duplication and
then showing how generics allow us to de-duplicate makes for a good
teaching flow, and I think it also helps make things clearer to students
coming from more dynamic languages that don't have an equivalent to
generics.

I also changed the `pick` fns to take a `bool` as the first argument
because I think that makes things slightly clearer/cleaner, but I'm not
married to that change either.
2025-03-03 10:03:10 -05:00

74 lines
1.9 KiB
Markdown

---
minutes: 5
---
# Generic Functions
Rust supports generics, which lets you abstract algorithms or data structures
(such as sorting or a binary tree) over the types used or stored.
```rust,editable
fn pick<T>(cond: bool, left: T, right: T) -> T {
if cond {
left
} else {
right
}
}
fn main() {
println!("picked a number: {:?}", pick(true, 222, 333));
println!("picked a string: {:?}", pick(false, 'L', 'R'));
}
```
<details>
- It can be helpful to show the monomorphized versions of `pick`, either before
talking about the generic `pick` in order to show how generics can reduce code
duplication, or after talking about generics to show how monomorphization
works.
```rust
fn pick_i32(cond: bool, left: i32, right: i32) -> i32 {
if cond {
left
} else {
right
}
}
fn pick_char(cond: bool, left: char, right: char) -> char {
if cond {
left
} else {
right
}
}
```
- Rust infers a type for T based on the types of the arguments and return value.
- In this example we only use the primitive types `i32` and `char` for `T`, but
we can use any type here, including user-defined types:
```rust,ignore
struct Foo {
val: u8,
}
pick(123, Foo { val: 7 }, Foo { val: 456 });
```
- This is similar to C++ templates, but Rust partially compiles the generic
function immediately, so that function must be valid for all types matching
the constraints. For example, try modifying `pick` to return `even + odd` if
`n == 0`. Even if only the `pick` instantiation with integers is used, Rust
still considers it invalid. C++ would let you do this.
- Generic code is turned into non-generic code based on the call sites. This is
a zero-cost abstraction: you get exactly the same result as if you had
hand-coded the data structures without the abstraction.
</details>