Blog: All About Arrays

Wherein we look at some possible language extensions around arrays (and lifetimes).

5 Likes

Allow me to write some comments here.

Despite I didn't explain this in a comment to the 1808, I agree that a very C-like alloca is too much under-engineered for Rust. So I'd like something more refined.

I don’t care too much if the syntax on top is [foo; n], alloca(foo, n) or alloca![foo, n], although the first one seems the most elegant.

I think that the syntax [foo; n] is a bad idea, because I think it's important to tell apart the cases where n is a constant or given by a compile-time function evaluation (so the array is fixed-size), from the case where n is the result of a run-time function (so the array is dynamically sized). This has caused problems for the growth of a similar feature in D language (despite in Rust this problem is less important because compile-time functions must have an annotation (const), unlike D language where every pure-ish function could run at compile-time). I prefer Rust to be more explicit and avoid this trouble.

Lifetime Ascription: sounds interesting, but I'd like to see some different examples of its usage, where it could be useful.

The benefit is that it would work with any IntoIterator type and make safe array/vec initialization really ergonomic.

This feature must be well designed, and what you write is still a little under-engineered.
What you want is something like Python lazy generators, so you can use them to build arrays, vectors, sets, and so on, or you can keep them as lazy generators (with the implicit usage of impl trait).

Such lazy generators have two advantages: the first is they often allow a better cognitive chunking ( Chunking (psychology) - Wikipedia ) of your code compared to the chains of iterators, and secondly you can use them in situations where the iterator chains are clumsy, that is where you want to nest two or more generators.

Example of code that's very readable in Python but becomes a little mess with a chain of Rust iterators:

foo = (x * y + 5 for x in xrange(10)
                 for y in bar()
                 if pred1(x) and pred2(y))

In my opinion well designed Rust lazy generators should allow to write something like that in a nearly equally readable way.

(0usize..10).into_iter().collect::<Vec<_>>()

The into_iter is redundant! I'd like to propose this as the alternative:

use std::iter::FromIterator;
Vec::from_iter(0usize..10)
4 Likes

The alloca function has not seemed to age well. It was never standardized in C or C++.

Its use is discouraged because implementations were often buggy, and I suspect it was too often associated with stack smashing; presumably those issues would never affect us!

If the size of the array is determined by untrusted data, then you may have given an attacker the ability to overflow your stack. (Of course, this is no different from, say, recursing deeply on untrusted data.)

What happens when a function that calls alloca is inlined into another function, say, into the body of a loop? Does LLVM correctly preserve the original semantics by dropping the alloca'd blocks in each iteration, or do they end up living as long as the stack frame of the function they were inlined into?

3 Likes