Generics<T> that accept String, &str and primitive value

Viewed 63

I apologize in advance as this may somewhat dumb question.
I was trying to create the function that simply return the bool result of
whether the given value is prime number or not.

I wanted the function to both accept the String, &str and primitive value.
Is there anyway that I can make this possible.

pub fn prime<T>(val: T) -> bool 
    where T: ToPrimitive {
    let val = val.to_i64().unwrap();
    for i in 2..=val {
        if val % i == 0 {
            return false;
        }
        
        if i*i >= val {
            break;
        }
    }
    true
}
3 Answers

I wanted the function to both accept the String, &str and primitive value. Is there anyway that I can make this possible.

You'd have to create your own trait to unify the feature: while Rust has grown the TryFrom trait, in the stdlib currently it's only used for faillible integer -> integer conversions (e.g. i64 to u8). String -> integer conversions remain on the older FromStr trait, and it's unclear that there's any plan to migrate.

I would agree with @cameron1024 though, doing this conversion in the callee increases the complexity without making much sense, if the callee takes a string I would expect it to handle and report validation / conversion errors to the source of the values. The prime of a string doesn't really make sense.

If you are concerned with performance, leave it as is. You are already accepting all integer types. Conversion from strings should happen elsewhere. (as already mentioned by the other answers)

That said, to actually fulfill your request, here is a horribly inefficient but simple solution to your problem :)

pub fn prime<T>(val: T) -> bool
where
    T: ToString,
{
    let val: u64 = val.to_string().parse().unwrap();
    for i in 2..val {
        if val % i == 0 {
            return false;
        }

        if i * i >= val {
            break;
        }
    }
    true
}

fn main() {
    println!("{:?}", prime(17));
    println!("{:?}", prime("17"));
    println!("{:?}", prime(String::from("17")));
}
true
true
true

On a more serious note ... your own solution and all of the answers convert the input into another type.

If you skip that and instead use the given type directly, you could potentially get a speed boost.

For that to work, though, I had to switch from i * i >= val to ..=val.sqrt().

pub fn prime<T>(val: T) -> bool
where
    T: num::PrimInt + num::integer::Roots,
{
    let two = T::one() + T::one();
    for i in num::range_inclusive(two, val.sqrt()) {
        if val % i == T::zero() {
            return false;
        }
    }
    true
}

fn main() {
    println!("{:?}", prime(17));
}
true

As a further note, you could rewrite the entire for loop with the all iterator method:

pub fn prime<T>(val: T) -> bool
where
    T: num::PrimInt + num::integer::Roots,
{
    let two = T::one() + T::one();
    num::range_inclusive(two, val.sqrt()).all(|i| val % i != T::zero())
}

fn main() {
    println!("{:?}", prime(17));
}
true

Firstly, do you really want to be able to check if strings are prime? You probably don't want someone to be able to write prime("hello").

Assuming you're fine with numbers, I'd just use Into<u128> as the trait bound:

fn prime<T: Into<u128>>(t: T) -> bool {
  let val = t.into();

  // rest of the logic
}

So what about strings and signed integers?

IMO, you should convert these at the call-site, which allows for better error handling, rather than just unwrap():

/// strings
prime("123".parse()?);
prime(String::from("123").parse()?);

/// signed ints
prime(u128::try_from(123i8)?);

This way, you can use ? to correctly handle errors, and make prime panic-free.


If you really want to not have to convert at the callsite, you could use a custom trait:

trait TryIntoU128 {
  type Error;
  fn try_into_u128(self) -> Result<u128, Self::Error>;
}

Then you could implement this on various types:

impl TryIntoU128 for String {
  type Error = ParseIntError;
  fn try_into_u128(self) -> Result<u128, Self::Error> {
    self.parse()
  }
}

// etc

Then you can use this trait bound for your prime function instead

Related