Menu
CoddyTech

Multiple Generic Types

Part of the Object Oriented Programming section of Coddy's Rust journey. Lesson 26 of 61.

Sometimes a single generic parameter isn't enough. What if you need a struct that holds two values, and those values might be different types? Rust lets you use multiple generic parameters by separating them with commas.

Here's a Pair struct that holds two values of potentially different types:

struct Pair<T, U> {
    first: T,
    second: U,
}

The <T, U> declares two independent type parameters. T and U can be the same type, or completely different: it's up to you when creating an instance:

let both_ints = Pair { first: 10, second: 20 };       // T=i32, U=i32
let mixed = Pair { first: "hello", second: 3.14 };    // T=&str, U=f64
let reversed = Pair { first: 100, second: "world" }; // T=i32, U=&str

When implementing methods, you declare all generic parameters on the impl block:

impl<T, U> Pair<T, U> {
    fn get_first(&self) -> &T {
        &self.first
    }

    fn get_second(&self) -> &U {
        &self.second
    }
}

This pattern is common when you need to associate two related but potentially different pieces of data, like a key-value pair, coordinates with different numeric types, or a result paired with metadata.

challenge icon

Challenge

Easy

Let's build a key-value storage system that demonstrates the power of multiple generic types! You'll create a KeyValue struct that can pair any two types together, perfect for associating labels with data, IDs with objects, or any other combination.

You'll organize your code across two files:

  • keyvalue.rs: Define a public generic struct called KeyValue<K, V> with two private fields: key of type K and value of type V. Implement methods to work with this struct:
    • A new associated function that creates a KeyValue from a key and value
    • A key method that returns a reference to the key
    • A value method that returns a reference to the value
    • A set_value method that replaces the current value with a new one
  • main.rs: Bring in your module and demonstrate how the same struct definition works with different type combinations. You'll create key-value pairs with mixed types to show the flexibility of multiple generics.

In your main file, create and work with the following key-value pairs:

  1. A pair with a string key and integer value (first input as key, second input parsed as i32 as value)
  2. Print the key and value
  3. Update the value to a new integer (third input parsed as i32)
  4. Print the updated value
  5. Create a second pair with an integer key and string value (fourth input parsed as i32 as key, fifth input as value)
  6. Print this pair's key and value

Your output should follow this format:

Key: {key}, Value: {value}
Updated value: {value}
Key: {key}, Value: {value}

For example, with inputs name, 42, 100, 7, and lucky:

Key: name, Value: 42
Updated value: 100
Key: 7, Value: lucky

Notice how the first pair uses KeyValue<String, i32> while the second uses KeyValue<i32, String>: same struct, different type combinations!

You will receive five inputs: a string key, an integer value, a replacement integer, an integer key, and a string value.

Try it yourself

mod keyvalue;

use keyvalue::KeyValue;

fn main() {
    // Read five inputs
    let mut input1 = String::new();
    std::io::stdin().read_line(&mut input1).expect("Failed to read line");
    let string_key = input1.trim().to_string();

    let mut input2 = String::new();
    std::io::stdin().read_line(&mut input2).expect("Failed to read line");
    let int_value: i32 = input2.trim().parse().expect("Failed to parse");

    let mut input3 = String::new();
    std::io::stdin().read_line(&mut input3).expect("Failed to read line");
    let replacement_int: i32 = input3.trim().parse().expect("Failed to parse");

    let mut input4 = String::new();
    std::io::stdin().read_line(&mut input4).expect("Failed to read line");
    let int_key: i32 = input4.trim().parse().expect("Failed to parse");

    let mut input5 = String::new();
    std::io::stdin().read_line(&mut input5).expect("Failed to read line");
    let string_value = input5.trim().to_string();

    // TODO: Create a KeyValue<String, i32> pair using string_key and int_value

    // TODO: Print the key and value in format: "Key: {key}, Value: {value}"

    // TODO: Update the value using set_value with replacement_int

    // TODO: Print the updated value in format: "Updated value: {value}"

    // TODO: Create a KeyValue<i32, String> pair using int_key and string_value

    // TODO: Print this pair's key and value in format: "Key: {key}, Value: {value}"
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming

Practice on your own: Online Rust compiler