6

I need something like:

fn my_convert<T, U>(v: &Vec<U>)->Vec<T>{
    v.iter().map(|&t|t).collect()
}

Of course, I suppose that only vector of builtin numeric types could be passed to the function.

The compiler tells me that I need to give the trait FromIterator for Vec

the trait std::iter::FromIterator<U> is not implemented for `std::vec::Vec

then, I added

impl<T, U> std::iter::FromIterator<U> for Vec<T>{
    fn from_iter<I>(iter:I)->Self where I: IntoIterator<Item=U>{
        Vec::<T>::new()    // just for test
    }
}

conflicting implementations of trait std::iter::FromIterator<_> for type std::vec::Vec<_>: note: conflicting implementation in crate alloc: - impl std::iter::FromIterator for std::vec::Vec;rustc(E0119)

As we all known, in C++, this kind of conversion is straight forward and intutive. I am a beginner of Rust. I don't know how to define a function like above. I even don't known if it is could be achieved in Rust because of the complexity of the trait for generic type.

Thanks!

Lucida
  • 65
  • 7
  • 2
    The error is actually saying that the type `T` is not the type `U`. Why would you expect mapping from `U` to `U` produce a `T`? The compiler does not know your intention so this is the best message it can do. – Kendas Nov 18 '21 at 03:29
  • I write a generic texture stuct whose data type is T to receive a generic Vec data to construct itself. T, U are expected as the common numeric type like u8 i32 f64 etc. – Lucida Nov 18 '21 at 03:39
  • Not really related to your question, but see [why you shouldn't take in an `&Vec`](https://stackoverflow.com/questions/40006219/why-is-it-discouraged-to-accept-a-reference-to-a-string-string-vec-vec-o). – Aiden4 Nov 18 '21 at 03:47
  • If T and U are related, you should express that in the signature - otherwise they could as well be "Clouds" and "LawsOfThermodynamics" – Kendas Nov 18 '21 at 04:24
  • Thank you all the same. These suggestions also help a lot Rust beginners like me. I will follow it. – Lucida Nov 18 '21 at 04:38

1 Answers1

15

The Rust From trait is your general purpose "how to get from A to B". If there's a reasonable way to go from U to T, then T: From<U> will be implemented. Consider

fn my_convert<T, U>(v: Vec<U>) -> Vec<T>
where T: From<U> {
  v.into_iter().map(T::from).collect()
}

Note that I do take ownership of the v vector. the From::from function consumes its argument, so we should also consume the vector. You could write a completely general version of this function that works with arbitrary iterators, and then it'll be easier to go back and forth between iterators which own their data and those that only borrow.

fn my_convert1<T, U, I>(v: I) -> Vec<T>
where T: From<U>,
      I: Iterator<Item=U> {
  v.map(T::from).collect()
}

But at that point, you'd be better off just writing v.map(T::from).collect() directly, and any reasonably proficient Rust programmer will instantly know what you're doing.

One point to note here, since you've mentioned that you're a C++ programmer. In C++, template functions can simply be written as-is, and then, when the caller instantiates a version of it, the compiler checks whether it's okay or not. So your proposed function in the OP works fine, since the compiler only checks it when it's called. Rust (and indeed most languages with generics) isn't like that.

In Rust, on the other hand, when you write the my_convert function, Rust checks at that time that all of the types line up. You can't write a function that only works some of the time. If you say my_convert works for all T and U, then it had better work for all of them, and Rust's type checker and borrow checker are going to verify that immediately. If not, then you'd better do as I did above and restrict the type signature. If you've used C++20 at all, you can think of traits as being somewhat similar to the "concepts" feature in C++20.

Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116
  • 1
    Thanks for you great answer and the following explanation. It is far more than a answer for a concret problem. This problem has blocked my serveral days and it's solved with your help. Thanks a lot. – Lucida Nov 18 '21 at 04:36
  • Sorry for trouble you again. I have another question: It seems that there is no a non-consume version of `From` trait (e.g. `FromRef`). I don't want to consume the input vector and just need a reference and copy from it. Must I define `FromRef` myself? @Silvio Mayolo – Lucida Nov 19 '21 at 01:43
  • 1
    There already is. It's called `T: From<&U>`. There's no need to make a new trait for it. The standard library defines several instances of that form, when it makes sense to do so. Of particular note, `String: From<&str>` is very often used to make an owned copy of a string slice. – Silvio Mayolo Nov 19 '21 at 01:56
  • 2
    I would even add that you can change `I: Iterator` to `I: IntoIterator`, and add `.into_iterator()` before the `.map(..)`. – Bamontan Aug 17 '22 at 13:04