Rust Anatomy of a generic function in Rust

It can handle different input types and thus it's called a generic function. The generic type is represented by the capital letter T in this example. T is an arbitrary placeholder. It could be have been another letter, X, Y or V...

Admin

2 min read

Consider the following function in Rust:

fn additems<T: Add<Output = T>>(i: T, j: T) -> T {
    i + j
}

It can handle different input types and thus it's called a generic function. The generic data type is represented by the capital letter T in this example. T is an arbitrary placeholder. It could be have been another letter, X, Y or V, but  when using T it can be easier to remeber that it refers to a "type".

I don't usually write code but I do enjoy reading and here the syntax of the function definition can be daunting at first. Let's have a look at a simpler version.

fn additems(i: i32, j: i32) -> i32 {
    i + j
}

We define a function called additems that accepts two arguments, each of 32-bit signed integer type and the function returns a value of the same type. If 64-bit integers or float values are passed on to the function it will throw a mismatched types error.

error[E0308]: mismatched types
  --> src/main.rs:13:24
   |
13 |     let sum = additems(5.0,6.0);
   |                        ^^^ expected `i32`, found floating-point number

error[E0308]: mismatched types
  --> src/main.rs:13:28
   |
13 |     let sum = additems(5.0,6.0);
   |                            ^^^ expected `i32`, found floating-point number

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0308`

By using generics we can make the function additems accept both float and integer values. Following an example from doc.rust-lang.org one would tend to try specifying the generic type T as <T> and start using it.

fn additems<T>(i: T, j: T) -> T { ... }

Alas, the compiler would complain about binary operation not possible on type B.

error[E0369]: binary operation `+` cannot be applied to type `T`
 --> src/main.rs:9:7
  |
9 |     i + j
  |     - ^ - T
  |     |
  |     T
  |
  = note: `T` might need a bound for `std::ops::Add`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0369`.
error: could not compile `namaste`.

The compiler is helpful enough to hint that we need to implement the std::ops::Add trait. Adding the following line use std::ops::Add; brings the Add trait into scope.

(More about these traits can be found in the Rust documentation about the overloadable operators module.)

The T type is then bound by the Add trait as <T: Add<Output = T>> and the complete block becomes:

use std::ops::Add;

fn main() {
   let sum = additems(5,6);
   println!("sum: {}", sum);
}

fn additems<T: Add<Output = T>>(i: T, j: T) -> T {
    i + j
}

The function additems can now accept two integers or two float values and do an addition operation on them.