ProgramGuru

Generics and trait bounds

Generics and trait bounds

A helper can compare marks of several numeric types without duplicating the same logic. A generic parameter names a type supplied at use sites. A trait bound states the operations that type must support, allowing the compiler to check the helper independently of a particular call.

What you’ll learn

  • State the operations a generic helper requires.
  • Recognize that partial comparison is not full numeric validation.

Generics and trait bounds in action

  1. Integer call: 70 versus 80

    The integer call uses a suitable integer type and returns 80.

    Integer call: 70 versus 80
  2. Float call: 77.5 versus 80

    The floating-point call returns 80.0, displayed as 80 by default formatting.

    Float call: 77.5 versus 80
  3. Bounded helper selects a value

    The same source logic is checked against the operations promised by the bounds.

    Bounded helper selects a value
The integer call uses a suitable integer type and returns 80. The floating-point call returns 80.0, displayed as 80 by default formatting. The same source logic is checked against the operations promised by the bounds.

Generics and trait bounds explained

T: PartialOrd + Copy requires comparison and copyable values. PartialOrd describes types for which comparisons may be partial rather than a complete order. This example returns an owned copy; a different API could borrow the selected input instead. A where clause can make longer bounds easier to read.

Work through the Rust program

Save the complete source as main.rs in the Rust environment. No standard input is needed.

Rust 2024 · complete program

fn higher<T: PartialOrd + Copy>(a: T, b: T) -> T {
  if a >= b { a } else { b }
}
fn main() {
  println!("{}", higher(70, 80));
  println!("{}", higher(77.5, 80.0));
}

Expected output

80
80

Follow the execution

The integer call uses a suitable integer type and returns 80. The floating-point call returns 80.0, displayed as 80 by default formatting. The same source logic is checked against the operations promised by the bounds.

Try the complete program in the Rust compiler. For tests, Cargo commands and local files, use the specified local environment.

Try a small variation

Your task: Can you pass arbitrary structs that have no comparison implementation?

Rust 2024 · complete program

fn higher<T: PartialOrd + Copy>(a: T, b: T) -> T {
  if a >= b { a } else { b }
}
fn main() {
  println!("{}", higher(70, 80));
  println!("{}", higher(77.5, 80.0));
}

Worked answer

No. The function requires PartialOrd. Implement an appropriate comparison or redesign the helper to accept a comparison operation.

Mistakes and limits to recognize

Floating-point NaN does not behave like an ordinary totally ordered value, so this simple helper is not a complete numeric validation policy. Generics can lead to specialized compiled code; they are not dynamic typing.

Check your understanding

Can you pass arbitrary structs that have no comparison implementation?
Choose one answer. You can try again.

Answer and explanation

No.

No. The function requires PartialOrd. Implement an appropriate comparison or redesign the helper to accept a comparison operation.

Take the idea into your next program

No. The function requires PartialOrd. Implement an appropriate comparison or redesign the helper to accept a comparison operation. Floating-point NaN does not behave like an ordinary totally ordered value, so this simple helper is not a complete numeric validation policy.

Further reading: the official Rust Book on generics and trait bounds.

Reaching the recap and completing the quizzes are separate milestones.

    Next lesson: Modules, crates and visibility →