Get/Detect the type of parameter in generic function

So I trying to build a generic function that do different things when a different parameter type is input.

For example, here is a common sample of generic function I can find, whose function swap the value of the input:

// A generic struct
struct Pair<T> {
    first: T,
    second: T,
}

// A generic function
fn swap<T>(pair: Pair<T>) -> Pair<T> {
    let Pair { first, second } = pair;

    Pair { first: second, second: first }
}

Or this one, which returns the largest value:

fn largest<T>(list: &[T]) -> T {
    let mut largest = list[0];

    for &item in list.iter() {
        if item > largest {
            largest = item;
        }
    }

    largest
}

However, neither example get/detect the input type, and I want to do something in relation to the type of the input value.
Say the function receive a parameter.

  • If the parameter is an i32, it multiplies by 2, print, and return the value
  • If the parameter is a char, it prints and return 0
  • If the parameter is a f64, it adds 1, prints, and return the value (as i32 is fine)

What are my options?
Thank you.

You can do separate impl blocks for different types.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=2791a4c57b8336dd5b4b4306fbca9aa9

What if I tried to place it into a function?

#[derive(Debug)]
pub struct Foo<T>(T);

impl Foo<i32> {
    fn run(self) -> Self {
        Self(self.0 + 1)
    }
}

impl Foo<String> {
    fn run(&self) {
        println!("hello {}", self.0);
    }
}
pub fn test<T>(inp:T){
    let foo = Foo(inp);
    println!("{:?}", foo.run());
}

This gives me an error of method run is not yet implemented upon calling the function test.

 method not found in `Foo<T>`

Since you've not added any type constraint on the type parameter T, it can literally be any type. What do you want to do if it's, say, std::fs::File?

I will only pass certain type of parameter to the function, so I am sure it is not gonna be std::fs::File.
But I will pass several different type of related parameters, and all want to do the same thing.
Maybe it's a bad idea to do what I do, and I will figure other way. Still, I just want to know how to do it.

Your willing is not encoded to the type system so the type checker should care such cases. In other words, you can encode such restriction into the type system.

trait CertainType {
    fn do_thing(self) -> Self;
}

fn do_something<T: CertainType>(input: T) -> T {
    input.do_thing()
}

impl CertainType for i32 {
    fn do_thing(self) -> Self {
        println!("{}", self * 2);
        self * 2
    }
}

impl CertainType for char {
    fn do_thing(self) -> Self {
        println!("{}", self);
        '0'
    }
}

impl CertainType for f64 {
    fn do_thing(self) -> Self {
        println!("{}", self + 1.0);
        self + 1.0
    }
}

Edit: fixed function names

2 Likes

I think there is a few spot between do_something and do_thing that are misplaced. It should be something like this:

pub trait CertainType {
    fn do_thing(self) -> Self;
}

pub fn do_something<T: CertainType>(input: T) -> T {
    input.do_thing()
}

impl CertainType for i32 {
    fn do_thing(self) -> Self {
        println!("{}", self * 2);
        self * 2
    }
}

impl CertainType for char {
    fn do_thing(self) -> Self {
        println!("{}", self);
        '0'
    }
}

impl CertainType for f64 {
    fn do_thing(self) -> Self {
        println!("{}", self + 1.0);
        self + 1.0
    }
}

Otherwise, this is a great answer, and I will select it as the solution. But hopefully you can do a bit correction for someone in the future to be less confuse.

Thank you.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.