I think the error messages (mostly) all indicate the same problem. My type parameter T needs to be some integer type. The code deals with small integers, so besides i8 and u8, any of the integer types will work. Is there a way to add a constraint that tells the compiler that T is any integer type? Am I really thinking about this in the right way?
Thank you.
Code and error message below.
use std::clone::Clone;
fn do_stuff<T>(nl : &[T]) -> Vec<T>
where T : Clone
{
let sz = nl.iter().cloned().product() as usize;
let mut mask : Vec<T> = Vec::with_capacity(sz + 1);
for i in 1..=(sz+1) as T
{
mask.push(i);
}
// Eliminate every multiple for each prime in the list
for p in nl
{
for i in 1..=(sz / (*p) as usize)
{
mask[i * (*p) as usize - 1] = 0;
}
}
mask
}
fn main() {
let mask = do_stuff(&[2, 3, 5, 7]);
for i in mask
{
print!("{} ", i)
}
}
Error messages:
error[E0308]: mismatched types
--> src/main.rs:8:18
|
3 | fn do_stuff<T>(nl : &[T]) -> Vec<T>
| - this type parameter
...
8 | for i in 0..=(sz+1) as T
| ^^^^^^^^^^^ expected integer, found type parameter `T`
|
= note: expected type `{integer}`
found type parameter `T`
= help: type parameters must be constrained to match other types
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
error[E0308]: mismatched types
--> src/main.rs:10:19
|
3 | fn do_stuff<T>(nl : &[T]) -> Vec<T>
| - this type parameter
...
10 | mask.push(i);
| ^ expected type parameter `T`, found integer
|
= note: expected type parameter `T`
found type `{integer}`
= help: type parameters must be constrained to match other types
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
error[E0308]: mismatched types
--> src/main.rs:16:43
|
3 | fn do_stuff<T>(nl : &[T]) -> Vec<T>
| - this type parameter
...
16 | mask[i * (*p) as usize - 1] = 0;
| ^ expected type parameter `T`, found integer
|
= note: expected type parameter `T`
found type `{integer}`
= help: type parameters must be constrained to match other types
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
error[E0605]: non-primitive cast: `usize` as `T`
--> src/main.rs:8:18
|
8 | for i in 0..=(sz+1) as T
| ^^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
error[E0605]: non-primitive cast: `T` as `usize`
--> src/main.rs:14:28
|
14 | for i in 1..=(sz / (*p) as usize)
| ^^^^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
error[E0605]: non-primitive cast: `T` as `usize`
--> src/main.rs:16:22
|
16 | mask[i * (*p) as usize - 1] = 0;
| ^^^^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
error: aborting due to 6 previous errors
Some errors have detailed explanations: E0308, E0605.
For more information about an error, try `rustc --explain E0308`.
I don't know that there is a great out-of-the-box way to do that. I think you are mostly thinking about it the right way, but there isn't such thing as an Integer trait that you could use to constrain T: Integer. And I don't think that as will ever work on a generic type parameter. So that wouldn't work either.
Here is an interesting way to do it, that may or may not be good for your use-case ( probably not ):
trait DoStuff<T> {
fn do_stuff(&self) -> Vec<T>;
}
macro_rules! impl_do_stuff {
($type:ty) => {
impl DoStuff<$type> for [$type] {
fn do_stuff(&self) -> Vec<$type> {
let sz = self.iter().cloned().product::<$type>();
let mut mask = Vec::with_capacity(sz as usize + 1);
for i in 1..=(sz + 1) {
mask.push(i);
}
// Eliminate every multiple for each prime in the list
for p in self {
for i in 1..=(sz / p) {
mask[(i * p - 1) as usize] = 0;
}
}
mask
}
}
}
}
// Add whatever types you want here
impl_do_stuff!(i16);
impl_do_stuff!(i32);
impl_do_stuff!(u16);
impl_do_stuff!(u32);
impl_do_stuff!(usize);
fn main() {
let mask = &[2, 3, 5, 7].do_stuff();
for i in mask {
print!("{} ", i)
}
println!("");
let mask = &[2u16, 3u16, 5u16, 7u16].do_stuff();
for i in mask {
print!("{} ", i)
}
println!("");
let mask = &[2usize, 3usize, 5usize, 7usize].do_stuff();
for i in mask {
print!("{} ", i)
}
}
The trick is to create a DoStuff trait that that has the do_stuff function and that is automatically implemented by using a macro for whatever types that you want it to work with.
But likely you are better off just requiring a specific type of int. Depends on what you are using it for.
You can't constrain generic types to other types. You can only require the types to implement a number of traits.
Generally functions that work with any integer types will be super tedious to specify, as you will have to require everything from std::ops and their outputs.
Consider using a macro, which behaves more like a C++ template, allowing any type that syntactically happens to work.
First, Thank everyone that responded. I will look at your suggestions.
After some pondering I remembered that Rust prefers to think in terms of what a type does, not what a type is. I was thinking, I want the type to be any of the integer types. Had I thought - I want T to do what integers do - I would have realized that I was over-constraining T.
The second thing Rust wants is for me to check all the details. In this case, Rust doesn't do implicit type conversions. So, this is what I came up with:
use std::convert::{From, Into};
use std::iter::Product;
fn do_stuff<T>(nl : &[T]) -> Vec<T>
where T : Copy // .cloned()
+ Product // .product()
+ Into<usize> // Conversions between T and usize
+ From<usize>
{
let sz : usize = nl.iter().cloned().product::<T>().into();
let mut mask : Vec<T> = Vec::with_capacity(sz + 1);
for i in 1..=(sz+1)
{
mask.push(i.into());
}
// Eliminate every multiple for each prime in the list
for p in nl
{
// Becasue into() can't infer the result without a hint
let pp : usize = (*p).into();
for i in 1..=(sz / pp)
{
mask[i * pp - 1] = 0.into();
}
}
mask
}
fn main() {
let mask = do_stuff(&[2, 3, 5]);
for i in mask
{
print!("{} ", i)
}
println!();
}
Note the use of .into().
I'm still not completely satisfied with this solution. I don't know how to say that the traits Into and From provide methods that are bijective.
What you've shown me is that I can do something like what I was thinking with a macro. I don't want to do that in this case but I now know it's possible. Now I'm really just trying to get a feel for Rust. I am arriving at the belief that this function is not a candidate for implementation with a type parameter.
So I think the answer is that I should not try to implement this function or one like it with a type parameter. I can understand that. Having experience with C++, I know that there are things you can do, but you shouldn't do.