The crucial trick is that it's
not just the constructor, it's everywhere which might allocate.
Rust's proposed allocator API (what the article calls future Rust) takes an allocator for constructors, but the effect is the type parameter is just inferred during construction, the same way if you say OK, make me a Vec of this array of Strings, Rust infers the Vec's type Vec.
Zig's standard library provides both conventional compound structures like those I described for Rust, and "Unmanaged" variants in which you must provide an allocator every time you do anything which might allocate, so e.g. addOne on ArrayListUnmanaged requires the allocator, which it will only actually use if adding a single element to the ArrayList exceeded its current capacity. It will assume this is the correct allocator to de-allocate the old backing storage and allocate new storage, so you can't use this design to move from one allocator to another.
Interestingly Zig's ensureTotalCapacity not only avoids the problem of C++ reserve where it destroys the amortized growth behaviour, but it actually insists on exponential growth even if that considerably outstrips the growth requested.
Say we've got 15 Foozles in an ArrayList with capacity 16 (or Rust's Vec or C++ std::vector). We know we want to put 20 more foozles in, for a total of 35, although maybe more.
Rust's Vec says OK, reserve(20), we were thinking of next growing from 16 to 32, but 35 won't fit in 32, so 35 it is. Capacity becomes 35.
Popular C++ std::vector implementations likewise will pick 35 here. But Zig's ArrayList says 16 + 8 + 8 = 32 not big enough, try 32 + 16 + 8 = 56. Capacity becomes 56!