let array = core::array::from_fn(|i| i);
assert_eq!(array, [0, 1, 2, 3, 4]);
Is it because of the assert_eq? Can it use the length of the second argument to infer the length all the way back up in the from_fn call? let array = core::array::from_fn(|i| i);
assert_eq!(array, [0, 1, 2, 3, 4]);
Is it because of the assert_eq? Can it use the length of the second argument to infer the length all the way back up in the from_fn call?People always complain about Rust's type system being verbose- well, this makes it less verbose. It knows what you need from the function call, so it doesn't make you say it. And as an added benefit, if the required type ever changes in a way that can still be generated by the provided code (eg. the function you're passing to needs a different length), you won't need to update any explicit type annotations at the call-site.
If that's ever undesirable - you want to be very precise about your code - well, then you can do that too by specifying the generic parameters explicitly.
let numbers = [0, 1, 2];
let doubled = numbers.map(|x| x * 2);
You wouldn't want to have to explicitly type out the length of the second array.> What's a sane/legit use case of such inferring in general?
Depends how you interpret "such inferring"
In other languages, this can be a brittle/expensive operation due to meh tooling. But with Rust, it tends to "just work".
Online viewers of source e.g. Github and such don't have all this information exposed yet, but they're incrementally getting there. For instance, "go to definition" works for Rust code in Github.
One could imagine a generic file format included as an artifact in source control, that online viewers could consume, to annotate spans of source code. Go to definition, show expanded type, etc.
let foobar = Default::default();
do_something_with(foobar); // where do_something_with takes a FooBar param
One I often use in unit tests: fn generate_ids<const N: usize>() -> [Id; N] { ... }
let [id_button, id_label, id_thing, id_other_thing] = generate_ids();
(before const generics were stabilized, the above code instead had multiple utility functions, eg generate_2_ids, generate_3_ids, etc) let mut x = HashMap::new();
x.insert(1, 2);
Note that nowhere did we need to specify the concrete type of the HashMap; the compiler sees that you eventually insert numbers into the map so it uses that information to fill in the generic type parameters for the key and value.I believe this is because of the signature:
pub fn from_fn<T, const N: usize, F>(cb: F) -> [T; N]
where
F: FnMut(usize) -> T,
While T is unconstrained, N is a usize, which is used in the closure, which we then re-use as the element. pub fn from_fn<T, const N: usize, F>(cb: F) -> [T; N]
where
F: FnMut(usize) -> T,
It takes a function with signature f(usize) -> T. So |i| i gets inferred to be a function taking a usize, and returning a usize (since we just give the argument straight back). So it infers [usize; 5] in the end.For example core::array::from_fn(|i| i) == [0i32] wouldn't even compile.
Which is in a way an even stronger showcase of Rust's type inference: the length of the variable is inferred from the constant array, but the type of the content of the constant array is inferred by the type of the content of the variable, which is inferred from the type of the closure.
assert_eq!(array, [0, 1, 2]);
If you tried both— assert_eq!(array, [0, 1, 2]);
assert_eq!(array, [0, 1, 2, 3, 4]);
—the assertions would never run because it wouldn’t even compile: error[E0277]: can't compare `[usize; 3]` with `[{integer}; 5]`