5 ms·
Really stuck out to me too. Does that mean that somewhere in there there are 32 different "overloads" (or whatever the right term is) of this functionality for
by MauranKilom 5y ago
Really stuck out to me too. Does that mean that somewhere in there there are 32 different "overloads" (or whatever the right term is) of this functionality for 1 to 32 elements? It feels so out of place...
- nynx 5y agoIt's not the case anymore.
- MauranKilom 5y agoOk, but why was it the case?
- ekidd 5y agoRust has two low-level "array" types: - &[Elem]: A "slice" of elements, represented as a pointer and a length. - [Elem; N]. A fixed-size array where the number of elements is known at compile time. This might be used for 2D or 3D coordinates, for example. Array slices have always supported arbitrary lengths. But arrays with fixed compile-time sizes are represented as entirely different types: it's an error to pass a [i32; 2] to a function that wants an [i32; 3]. The size of the fixed-size array is part of the type. And until very recently, Rust's type system didn't support defining methods on [Elem; N] that were generic over N. This is a feature that tends to be added later in many languages that support generics, not just Rust.
- rwmj 5y agoThanks, I think this is the clearest explanation in the thread.
- hderms 5y agoIt actually isn't really that unusual to have things like this in a statically typed language. Scala and Haskell have similar limitations with tuples
- masklinn 5y agoWithout const generics, traits had to be implemented on every arity. Even with macros, there has to be a limit somewhere in the number of impls you allow as the implementations are explicit and eager. 32 was the limit selected.
- simias 5y agoIt was normally implemented with macros to hide the boilerplate, but I agree that it wasn't very elegant. You can understand why many of us were very happy to finally get const generics...
- tialaramex 5y agoYes. This is untidy but effective. Here's what's left (this was greatly reduced by const generics already, as another poster has explained): https://doc.rust-lang.org/src/core/array/mod.rs.html#383 https://doc.rust-lang.org/src/core/array/mod.rs.html#383 That macro implements Default, but only for arrays of size 1 to 32. It doesn't make any sense to talk about a Default array of size 0, the array is empty, there's no "default" content at all. You can see the same verbosity for places where in some other languages you'd be able to just say all these things just work with any number of elements in a tuple, and Rust has chosen "up to 12" instead because it cannot express that. https://doc.rust-lang.org/src/core/tuple.rs.html#100 https://doc.rust-lang.org/src/core/tuple.rs.html#100 The top half of that file is explaining to Rust's macro language how to do various things for an N-tuple, and then the bottom half call that macro for the 1 through 12 sizes. If you really need a 16-tuple (unlikely, but...) you could copy paste the code to make one with the same features. This isn't compiler support, so you could just do it yourself.
- estebank 5y ago> If you really need a 16-tuple (unlikely, but...) you could copy paste the code to make one with the same features. This isn't compiler support, so you could just do it yourself. There's one wrinkle in that strategy in that you might run afoul of the orphan rule[1][2] where you can't write: impl ForeignTrait for ForeignType {} because if we did it would be brittle and changes to an external crate could make your unchanged crate stop compiling. So you're restricted to either implementing your own trait or any trait on a type you declared. To "get around this" you can use the new-type pattern[2]: struct MyArray<T>([T; 33]); impl ForeignTrait for ForeignType {} [1]: https://github.com/Ixrec/rust-orphan-rules https://github.com/Ixrec/rust-orphan-rules [2]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=1c59e381266cb2699d7f32e52a4538da https://play.rust-lang.org/?version=stable&mode=debug&editio...
- tialaramex 5y agoMy error. Good point about the orphan rule. Because I was writing about Tuples what I was talking about is actually impossible in your own code I think, because tuples are inherently foreign. Even though the code in std looks tame, you are forbidden from doing this elsewhere. Even for a hypothetical local type MyType you are forbidden from attempting: impl ForeignTrait for (MyType,u8) The compiler will conclude that ForeignTrait is foreign, of course which is what we expected, but (MyType,u8) is also foreign even though MyType is local and so the orphan rule forbids this definition. Shame. It doesn't make any sense to sidestep with new type because if you didn't want a tuple you could already define whatever type you wanted and this isn't an issue anyway, whereas if you do really want a tuple then a new type isn't a substitute.
- kzrdude 5y agoFormal systems are rarely perfect :)