Rust 1.51
blog.rust-lang.org
blog.rust-lang.org
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/
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.
> 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.
This is great for keeping memory on the stack when N is small, yet allow heap memory overflow for larger N.
That might explain why Rust topics hit the front page so often.
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.
Horror!
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.
meh. Doing tricky stuff with them is far easier as TS is a dynamic language, but the fact that enum variants are each a variant makes constructing them really easy:
let array = [1, 2, 3];
let some_array: Option<i32> = array.iter().map(Some).collect();JS isn't known for being great at encapsulation either, there could be some benefits for exceptionally large front-ends.
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-combi...
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).
foo.windows(2).map(|window| match window {
[l, r] => ...,
_ => panic!(...),
})...
I would like to write: foo.windows::<2>().map(|[l, r]| ...)...for &[l, r] in data.array_windows() {
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 pages32 seems so normal that it made me question why the default was 33, to include 0-32, but whatever heh. Glad to see this!
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.
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.
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-bet...
#[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.(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).
func(a) func(a,b)
Etc... some languages have really weird bit :)
I guess I can deprecate it now!
[1] https://blog.rust-lang.org/2021/03/25/Rust-1.51.0.html#array... [2] https://docs.rs/array_iterator
> This is not an officially supported Google product
Can you share you your use case?
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
Does rust have compile time protection against stack overflow? (... and what about recursion? is the stack dynamically resized at runtime?)
For example: what if I decide at some point to create a Vec of this struct? I don't want each of those elements then also putting their internal buffers into separate allocations on the heap, when the Vec itself is already on the heap. I want the struct to be as "flat" as possible, and to only allocate for the sake of the stack as a final step when the use-case demands it
Recursion can blow your stack.
I could very easily make the word size generic, but not the depth. So I either had to make the depth dynamic (pretty expensive if you have hundreds of thousands of FIFO operations per second) or hack around it somehow.
Now I can just make the depth a generic parameter.
You can also abuse these types of generics to force the compiler to generate specialized versions of some methods in order to get better performance (at the cost of binary size) without having to manually create a bunch of variants of the same code.
With const generics, the library provided implementations for all `[T; N]`, so my code worked out of the box with no headache.
Well, array literals are very convenient in some areas.
Matrix types can be cleaned up across projects, one widespread area being 3d graphics. Previously, you either had to create a separate type for each matrix size, or use a backing vector (of vectors); the first solution is ugly (because of redundancy), the second is potentially underperformant (in cases where one wants to avoid dynamic allocations as much as possible).
There is support but there is not too much information about it. Anyone had good/bad experience?
There is also an interesting crate that abstracts over different instructions and does runtime detection [2].
AVX512 and F16C instructions are the main exceptions I'm aware of that still need to be stabilized
So now the compiler will generate calls to memcpy() for accesses to unaligned elements in packed structures like the one in the example?
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....
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.
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?