10 | fn zero<T>() -> Outer<T>
| - expected this type parameter
...
14 | Outer { a: 0 }
| ^ expected type parameter `T`, found integer
|
= note: expected type parameter `T`
found type `{integer}`
OK, so I understand why this doesn't work: I've not told the compiler that T is always going to be a numeric type. But how do I tell the compiler this? I have googled around and people have suggested "use num::Integer but the docs aren't super clear for my use case. I tried this:
use num::Integer;
struct Outer<T>
where
T: Integer,
{
a: T,
}
fn zero<T>() -> Outer<T>
where
T: Integer,
{
Outer { a: 0 }
}
But I get basically the same error. What am I missing here?
When you bound T: Integer, that doesn’t mean integer literals can automatically coerce to T. You need to use a constructor on the trait, like num::NumCast::from.
In your specific case, you can use T: Zero and return Outer { a: T::zero() }.
In a function like fn zero<T>(), the caller chooses the type for T. You are not allowed to decide. The real type T is already decided for you and it's final, even before your function starts running.
Traits in Rust are "open", and can be implemented on new types. They can be implemented in the future, on types that don't even exist yet, and all existing code with its trait bounds is guaranteed to continue to work. Rust doesn't know that num::Integer is meant to be implemented only on integers. As far as Rust understands, it's a num::FooBar trait, and tomorrow it could be implemented on arrays of strings! So Rust doesn't know how to convert 0 to a who-knows-maybe-it-will-be-an-array-of-strings-tomorrow type.
Since you don't choose what T is, and there's nothing in the language limiting what Integer can be implemented on in the future, your code has to work with all types, even types that aren't integers and don't exist yet.
Also, 0 is a literal that doesn't have a type yet. Its real type may be inferred from the context, or assumed to be i32 as a fallback, but there's no way to implement traits on just 0. You'd need to pick some type like u8, and then add a type bound like T: From<u8> and then use T::from(0) or T::from(0_u8). T: Integer + From<u8> still won't tell rust that T is an integer, but at least will require callers of your function to use it only on types that can be converted from u8.