Well, as usual, I'm having issues converting a function to accept generic types. The following code works just fine whether I pass/return a string, an integer or whatever:
fn main() {
let item: String = choose_item();
println!("\nThe item you chose == {}", item);
}
pub fn choose_item() -> String {
"This is a string".to_string()
}
But as soon as I try to make the function generic, I get errors. The following code doesn't work:
fn main() {
let item: i32 = choose_item();
println!("\nThe item you chose == {}", item);
}
pub fn choose_item <T>() -> T {
34
}
Here is the error the compiler throws:
6 | pub fn choose_item <T>() -> T {
| - - expected `T` because of return type
| |
| expected this type parameter
7 | 34
| ^^ expected type parameter `T`, found integer
|
= note: expected type parameter `T`
found type `{integer}`
So, once again, could someone help me find my error? Thanks. Maybe someday I'll get this generic stuff learned.
Which is what I thought I was doing with this line:
let item: i32 = choose_item();
Since I defined as i32 the variable that calls the function, my assumption was that the compiler would then infer that the generic type <T> is also i32. Obviously not, but how is my thinking wrong?
Your assumption is correct, but the function needs to work whenever you call it, not just this specific location. A function that hasn't been used incorrectly but could be used incorrectly isn't allowed.
Okay, I think I get that I'm still asking the function to choose the return type. @geebee22 offered a possible solution above, but that would tie me into using only u8 where the idea is to be able to use any type whether for String or f64 or whatever. My code snippet is greatly simplified. The actual function I'm attempting to build will choose an element from a vector (of any type) and then return it. Any ideas how to approach this?
The code I posted will work for any type that can represent all u8 values, including f64 and any integer type etc.. Your function returned 34, which is why it has to be some kind of number. But I think you probably don't have quite the right idea of what generics do... maybe you need to explain more what problem you are trying to solve.
Your choose_item wants to say: I return some type of value, but I'm not telling you what type. The way you express this in the Rust type system is
pub fn choose_item() -> impl Display {
"This is a string".to_string()
}
What your original signature says is: I can return any type you ask me for. The problem with this that you can't soundly conjure a value of an arbitrary type, much less a useful value. If some code calls your function asking for a value of type ThirdPartyCrateTypeWithNoPublicConstructors you definitely can't satisfy that request.
You can use any trait with the -> impl Whatever syntax. Of course, the actual type you return has to implement that trait! I chose Display because it corresponds to how your code uses the return value of choose_item, Display being the trait that controls "{}" formatting. The catch is that when you're calling the function, you can only use the return value in a way that's supported by the trait you pick. If you use impl Display, you can't call String::as_str on the return value, even though the function body always returns String.
The other main thing to know about -> impl Whatever (sometimes called "return-position impl Trait" or RPIT) is that you really have to pick one return type. You can't do this:
fn choose_value() -> impl Display {
if today_is_tuesday() {
"here's a string".to_string()
} else {
17
}
}
I don't know your history, but you might have the C++ template model in mind, where essentially the T is substituted then the types are checked.
In Rust (as well many other languages that use the term "generics") the declaration of the function is checked independently of it's use.
This means you need to use traits to actually do anything with the type parameter other than just move it around, including creating a value in the first place!
Hopefully this helps if you're still trying to get to grips with the earlier answers.