10 ms·
Yes, it's inferring that `array` must be an `[i32; 5]` because there exists an impl of `PartialEq` for `[T; N]` against another `[T; N]`, so `N` in `::from_fn`
by dymk 4y ago
Yes, it's inferring that `array` must be an `[i32; 5]` because there exists an impl of `PartialEq` for `[T; N]` against another `[T; N]`, so `N` in `::from_fn` must be 5.
- brundolf 4y agoThat rocks
- zerr 4y agoWhat's a sane/legit use case of such inferring in general?
- brundolf 4y agoI think the one here is legit (perhaps more realistic would be where you're passing the new array to an actual function that requires a certain array length). 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.
- game-of-throws 4y agolet 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.
- tomjakubowski 4y agoThat isn't really inferring the size of the array though. [T; N]::map(impl FnMut(T) -> U) defines its return type as [U; N]. N is fixed at the original array literal. https://doc.rust-lang.org/std/primitive.array.html https://doc.rust-lang.org/std/primitive.array.html
- brundolf 4y agoIt's inferring it from the array literal in the first place, but it is a different kind of inference from the original one. But I think that's what the question was asking for: > What's a sane/legit use case of such inferring in general? Depends how you interpret "such inferring"
- dymk 4y agoIt's inferring the size of `doubled`
- deleted 4y ago[deleted]
- Ar-Curunir 4y agoYou general type inference, or inferring the array length? The latter follows from the former, so it would be kind of a weird hole in the language if type inference worked everywhere except array lengths.
- zerr 4y ago"Scattered through multiple lines" inference :) Such code makes another programmer analyze multiple lines to deduce types in the head.
- brundolf 4y agoIn practice, type inference works best when paired with editor integration. Most languages/editors will give you hover-overs with inferred types, but some like rust-analyzer have started going further (the greyed-out text here is all inlaid by the editor): https://user-images.githubusercontent.com/3971413/96668192-1c51d500-13b7-11eb-9f17-cb5fe6be4605.png https://user-images.githubusercontent.com/3971413/96668192-1...
- dymk 4y agoThe tooling ecosystem, for instance rust-analyzer, make this not an issue if you're reading the code in an environment that supports it. I have rust-analyzer to display inferred types when I hold `control+command`. 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.
- PoignardAzur 4y agoA few use cases: 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)
- kibwen 4y agoIt's not anything specific to arrays, it's just how type inference works. Here's another example: 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.
- sophacles 4y agonit: wouldn't it be usize since the only way to get T is from the bare numbers which default to usize?
- deleted 4y ago[deleted]
- steveklabnik 4y ago"bare numbers" don't default to usize; integers default to i32 and floats to f64.
- aendruk 4y agoIt does seem to be choosing [usize; 5] in this case though. https://rust.godbolt.org/z/qasxnrTf9 https://rust.godbolt.org/z/qasxnrTf9
- steveklabnik 4y agoYes, that is true. If you use a literal array, you get i32 for the element type. 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.
- sophacles 4y agoOK this is just kinda funny - I was right, but only by accident. For some reason I thought that the literal defaulted to usize not i32. The fact that this path makes my assertion of usize correct is pretty cool though. Thanks for diving into it!
- orlp 4y agoIn this case the "bare numbers" do get inferred to be usize due to the signature of from_fn.
- orlp 4y agoSorry but the typing part is incorrect. Look at the signature of from_fn: 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.
- dymk 4y agoGood catch. The parent comment is interested in how the array length is inferred though, so I'm going to consider the explanation close enough (that it's an i32 or usize isn't particularly relevant).
- wongarsu 4y agoYou are right. The parameter given to the closure is the array index, which is usize. Since that's returned straight back the returned value is also of type usize, and the integers in the array in the assert are inferred to also be usize. 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.