9 ms·
Rust 1.51
- wyldfire 6y ago> For a long time, even the standard library methods for arrays were limited to arrays of length at most 32 due to this problem. This restriction was finally lifted in Rust 1.47 - a change that was made possible by const generics. I haven't been following every single update, so it's good to know that this constraint is no longer present (and has been gone for a bit).
- ibraheemdev 6y agoIt was removed from that standard library because std is built on nightly (which had const generics a while ago). Now that const generics are stable, crates can take advantage of this too. Previously they were using macros to generate the 32 array implementations.
- adkadskhj 6y agoI was bit by this yesterday. I had a 32 array, but the implementations only went up to 31 (with 0, so 32 in total). Found that quite amusing. 1 day away, 1 array element away. 32 seems so normal that it made me question why the default was 33, to include 0-32, but whatever heh. Glad to see this!
- littlestymaar 6y ago> It was removed from that standard library because std is built on nightly IIRC std and the compiler are built with the previous stable version of the compiler but with some special flag that allow using unstable features for bootstrapping.
- Lev1a 6y agoIIRC it goes further than that. Previous stable (0) compiles current source (1) into a temporary new stable (2), then (2) is used to compile (1) into the actual release version (3). Could be wrong though, it has been a long time since I stumbled over a description of the release process.
- tgb 6y agoWhat are you quoting? I can't find that in the article linked here and I don't understand what it's saying. Surely Rust didn't cap arrays at length 32?
- hctaw 6y agoMethods for arrays were capped at 32. So for example #[derive(Copy, Clone, Debug)] struct S { a: [i32; 33], } Would not compile, since Copy, Clone, and Debug were not implemented for arrays larger than 32.
- est31 6y agoNit: Copy has always been a builtin auto trait so it has always been implemented. You are right for the other traits. (also as other commenters point out, std builtin types like Clone etc have already been supporting larger array sizes because of the standard library's ability to use unstable features, but before const generics were available as an unstable feature, that restriction very much did exist, and now crates from the ecosystem can use it too).
- wyldfire 6y ago> If you'd like to learn more about const generics you can also check out the "Const Generics MVP Hits Beta" blog post for more information about the feature and its current restrictions. The article links to more details in that post [1], that's what the citation was from, sorry. I thought it was conspicuous that they had '32' in the example because I remembered it as a limitation before. No, arrays were not limited in length but IIRC generic types weren't able to make an array size generic across arbitrary counts. [1] https://blog.rust-lang.org/2021/02/26/const-generics-mvp-beta.html https://blog.rust-lang.org/2021/02/26/const-generics-mvp-bet...
- anchpop 6y agoArrays could be any length, but generic functions were only generic for arrays of length 32 or lower.
- exdsq 6y ago
- throwaway894345 6y agoI'm a bit of a noob, but could const generics allow for a const generic `.windows<const WIN_SIZE: usize>()` function that returns a fixed length array instead of a slice? E.g., instead of: foo.windows(2).map(|window| match window { [l, r] => ..., _ => panic!(...), })... I would like to write: foo.windows::<2>().map(|[l, r]| ...)...
- ogoffart 6y agoYes: https://doc.rust-lang.org/stable/std/primitive.slice.html#method.array_windows https://doc.rust-lang.org/stable/std/primitive.slice.html#me...
- throwaway894345 6y agoPerfect, that's exactly what I want!
- adrian17 6y agoNote that you don't even need to supply the size, as it'll get deduced: for &[l, r] in data.array_windows() {
- gpm 6y agoYes, but note that if you do that the parameter has to be constant, you can't do foo.windows::<runtime_expr>()
- throwaway894345 6y agoYeah, that's fine. I'm making a static site generator and I want each post to have optional links to the next and previous posts, so something like: let pages = posts.windows::<3>().map(|[prev, this, next]| Page { item: this, prev: Some(to_url(prev)), next: Some(to_url(next)), }); // then attach first and last pages
- qw3rty01 6y agoFINALLY min_const_generics in stable rust!
- simias 6y agoIndeed. Time to rewrite all of my code that used hacks to work around this limitation.
- OrangeTux 6y agoI think I understand what const generics are. But I fail to see a use case for it, other than using them in array literals and abstractions over arrays. Can you share you your use case?
- adkadskhj 6y agoNot OP, but my use case is serializing an array. Implementations were provided by the library for serializing arrays of `T` for lengths 0 through 31. I had an array of `[T; 32]`, so i couldn't serialize my array. With const generics, the library provided implementations for all `[T; N]`, so my code worked out of the box with no headache.
- brundolf 6y agoIn the graphics programming I've done as a hobby, if I have a struct that contains a buffer for an image (say 1024px x 1024px = a buffer array of length 1048576), and have functions that operate on that buffer, etc, I either have to: 1) Create a Vec (separate allocation) 2) Make 1024 or 1048576 a global constant that every relevant struct and function references directly (whole program must use the same size at once; forget about exposing it as a library) With const generics I can make all that code generic over the buffer resolution, meaning I can trivially use multiple different versions within the same build, without doing extra allocations, and I could even expose that code as a library for others to use at whatever resolution they wish
- adamch 6y ago
- wyldfire 6y ago> These two macros now let you safely create unaligned pointers. So now the compiler will generate calls to memcpy() for accesses to unaligned elements in packed structures like the one in the example?
- kzrdude 6y agoGood question. I think the answer is no, and that `let value = *ptr;` is UB if the pointer is misaligned. The right way to go is `let value = ptr.read_unaligned();` But the docs will say this: > Accessing unaligned fields directly with e.g. packed.unaligned is safe however. https://doc.rust-lang.org/nightly/std/ptr/fn.read_unaligned.html https://doc.rust-lang.org/nightly/std/ptr/fn.read_unaligned....
- coolreader18 6y agoCreating unaligned references is UB, so it's illegal to take a reference to a packed struct field. However, even if you just wanted to create a raw pointer to that field, you would've had to do `&packed.f2 as *const u16`, which is technically UB since you do create a temporary reference before casting it to a pointer. I'm sure one way to solve that is to just allow that as a special case; say that `&<expr> as *const _` semantically doesn't actually create a reference. But, the way the lang team went is to have a separate macro that transforms `ptr::addr_of!(<expr>)` to something like C's `&<expr>` operator, so that it creates a raw pointer directly instead of a reference.
- kzrdude 6y agoMaking the existing syntax - `&<expr> as *const _` like you said - just work would be far and away the best solution. Otherwise we have a 5-year transition period before all code out there that touch this get the memo.
- csomar 6y ago> Cargo's New Feature Resolver This couldn't come sooner! I had to fork a particular dependency because Cargo was pulling the wrong dependencies. It drove me a bit crazy before I figured out the dependencies causing the issues. [1]: https://users.rust-lang.org/t/cargo-build-failing-when-combining-two-dependencies/56670 https://users.rust-lang.org/t/cargo-build-failing-when-combi...
- xiphias2 6y agoThis may be a stupid question, but will the array of const generic structures stack allocated or dynamicly? Or are there both versions?
- est31 6y agoThe size of the arrays will still be determined at compile time to a specific value. This only allows code to be written that is generic in terms of the size of the array, but it still requires some other place of the codebase to supply an actual size for it to become part of the executable.
- rkangel 6y agoAs with any other rust value it depends how you create it. If you just to "let x = ..." in your function then it'll be on the stack. If you do "let x = Box<...>" then it'll be on the heap.
- GolDDranks 6y agoIt's not a stupid question! The short answer is: the compiler doesn't care. An array of type [T; N] (where T is the type of the elements and N is the length of the array) can be allocated on the stack or in the heap (using Box<T>, as stated in sibling comments). If you access it by value, it will be copied as-is, so be careful with large arrays! You mostly access them behind a reference, but in that case, the type of the reference will be &'a [T; N] where 'a is the lifetime of the referent value. The type system by itself doesn't actually care whether the value lives in heap or stack.
- brundolf 6y agoTo refine what another comment has said: the array will be local (not across a pointer) to whatever struct or block it's contained in. So if it's contained in a heap-allocated struct (a Box or a Vec for example), it will be on the heap (within that struct, not behind another pointer). If it isn't contained within a heap-allocated struct, it will be on the stack (or if it's a const, it'll be in a static location). This is why arrays in Rust must always have a statically-known size: because they themselves do not create heap-allocations, so the compiler must know how much space to set aside for them. And that fact is why const generics are such a big deal: they allow a certain kind of variability in that static size for different invocations of the same piece of code, so long as the size can still be determined for each usage at compile time.
- adamch 6y agoLast night I was writing a blog post about how to use const generics. I included instructions on installing Rust Beta, and setting your directory to use the Beta compiler instead of Stable. I started to write a disclaimer like "By the way, this will be stabilized in a future version of Rust, probably on..." and realized that const generics were being stabilized in less than 9 hours! So I removed all that stuff. If you're interested in some concrete examples of using const generics to write faster code, my post on them is here: https://blog.adamchalmers.com/grids-2/ https://blog.adamchalmers.com/grids-2/
- reader_mode 6y agoThe good old Vector<T, N> from C++. For all the pain points C++ has, template metaprogramming was always so satisfying when you got it to work. Satisfying in "I feel really clever for solving this so generally and with no runtime overhead" way, but also being completely aware that another person reading the code and having to maintain it migh order a hit on me over darkweb.
- nayuki 6y agoDo you mean std::array<T,N>? ( https://en.cppreference.com/w/cpp/container/array https://en.cppreference.com/w/cpp/container/array )
- reader_mode 6y agoVector<T, N> an Matrix<T, N> is a fancy way to do small linear algebra types - like 2,3,4 vector and 3x3/4x4 matrix - you implement simple operations like add, multiply and dot products with loops and the compiler will unroll it and then you add some specific overloads for stuff like cross product, etc. Saves you having to reimplement it for every N. It's the simplest case of this kind of metaprogramming and feels good once you discover this kind of generalisation.
- nindalf 6y agoIt still says > This is gonna require some const generics stuff that isn't actually stabilized yet. This means, for the first time in my life, I'm going to have to use... Nightly Rust. I heard somewhere that it's stable now.
- ChrisSD 6y agoWow, const generics were three years in the making. Longer even if you count the work leading up to the RFC. Rust's pace of change can seem very slow at times but I'm glad they take the time to do it right.
- kzrdude 6y agoEnormous strides have been made in compile time evalutation since then, and other interesting fields like MIR optimizations (optimizations that happen in rustc and not the llvm part).
- andrewflnr 6y agoI think the way that Rust releases keep hitting the front page of HN helps create an illusion that the language is changing faster than it really is. There's a sense in which a front-page worthy release is expected to be a big one, and if that happens all the time, there must be a lot of big changes, right? That said, I'd have to say it's worth it in the case of stabilizing const genetics.
- vardump 6y agoI think there's been quite a bit of interest to adopt Rust in production lately. That might explain why Rust topics hit the front page so often.
- lumost 6y agoaye - it opens the door to native computing to a large number of devs who have only worked in modern GC'd languages. The cross-compilation targets of rust are highly appealing to those whose work may be 1. performance sensitive 2. want to run one stack across frontend/backend/native apps 3. don't want to learn multiple languages for multiple activities. It's a pretty compelling pitch.
- azangru 6y agoWeb frontend in Rust? Horror!
- lumost 6y agoor is it? could take some nice performance gains/stability gains for SaaS apps with heavy visualization components. JS isn't known for being great at encapsulation either, there could be some benefits for exceptionally large front-ends.
- dmit 6y agoHow come? Wasm is only at ~92% browser adoption right now (per https://caniuse.com/?search=wasm https://caniuse.com/?search=wasm), but certain classes of apps can easily take that 8% hit for all the benefits over the status quo languages that Rust brings. You get painless sharing of domain types between client and server via serde; you still get native async/await support; you get more powerful data modeling features than even Typescript can provide (although Typescript's union types are definitely much more ergonomic). You get a standard library that's not a joke. You don't have to wait until proposal-temporal gets ratified and implemented in order to have sane date/time handling (recently hit Stage 3 btw, woo!). Honestly, only ScalaJS can match all of those benefits, as far as I can tell after examining a dozen of languages targeting the web. So yeah, I'm super curious to hear what problems you encountered with Rust as a web frontend language.
- sony_james2 6y agoWould you recommend switching to Rust from C++ when working with Intrinsics (like SSE, AVX)? There is support but there is not too much information about it. Anyone had good/bad experience?
- papaf 6y agoI know very little about SIMD but a lot of crates I have used support SIMD acceleration. For an overview see [1]. There is also an interesting crate that abstracts over different instructions and does runtime detection [2]. [1] https://lib.rs/keywords/simd https://lib.rs/keywords/simd [2] https://docs.rs/simdeez/1.0.6/simdeez/ https://docs.rs/simdeez/1.0.6/simdeez/
- kzrdude 6y agox86/-64 intrinsics are stable (since Rust 1.28, i.e many releases back), but inline asm is not. You could maybe try something simple and representative to get your own idea.
- jsheard 6y agoNot quite all of the x86 intrinsics are stable but yeah, the common SSE/AVX stuff has been in for a while AVX512 and F16C instructions are the main exceptions I'm aware of that still need to be stabilized
- kevincox 6y agostd::array::IntoIter[1] is great. I used to maintain a crate[2] to do this. Of course doing so requires unsafe so I am happy to move that danger into std:: I guess I can deprecate it now! [1] https://blog.rust-lang.org/2021/03/25/Rust-1.51.0.html#arrayintoiter-stabilisation https://blog.rust-lang.org/2021/03/25/Rust-1.51.0.html#array... [2] https://docs.rs/array_iterator https://docs.rs/array_iterator
- ibraheemdev 6y agoWhy the disclaimer? > This is not an officially supported Google product
- kevincox 6y agoBecause I worked at Google when I first made it.
- jkarneges 6y agoInteresting to see the Wake trait introduced. It appears to make it possible to create a Waker implementation without requiring unsafe code, at the cost of requiring the use of Arc. The way the trait is used by the standard library is amusing. A trait bound is used to ensure a valid implementation is provided, but then the trait wrapping is thrown away and the inner value is only kept around as a raw pointer. That pointer is then casted back to the trait when a method needs to be invoked on it. I guess this works because the methods use Arc<Self> as self. The trait implementation basically carries its own boxed data with itself, so whoever "owns" the trait implementation doesn't have to worry about managing a dynamically sized value. Has anyone seen that pattern used anywhere else?