On 13/06/2012 4:15 PM, Niko Matsakis wrote:

*Implications for writing efficient Rust*

I figured I'd just start with the implications for writing Rust.
Currently, to build up a vector, we rely upon an idiom like:

let mut v = [];
for some loop { v += [elt]; }

Yeah. After we lost the one-element-append rule, this never really read well anyway. Happy to see it go (at least in that form).

Now, often, such loops can (and should) be written using a higher-order
function (e.g., map, filter). But sometimes not. In such cases, under
the new regime, the recommended idiom would be:

let dv = dvec();
for some loop { v.push(elt); }
let v = dvec::unwrap(dv); // if necessary, convert to a vector

Actually the name `dvec()` (dynamic vector) will probably change—perhaps
to vecbuf? mvec? suggestions welcome—but you get the idea. The same
would eventually apply to building up strings.

It's funny, this is pretty clearly a stack (growth is optimized only at one end), so I'm of half a mind to just call it a stack, but something kinda balks at the notion. Not sure why.

What disappoints me about all this is that the language is now dependent on unsafe library code in order to do asymptotically fast aggregate types at all. The += optimization and vec doubling-allocation was the "one primitive" related to aggregate types that the compiler was filling in for us safely, before, for the sake of building more-efficient containers on top. Now pretty much every primitive container that has a doubling-store growth pattern is going to involve unsafe code. That's a bitter pill, but I guess it was true before too, it was just unsafe code with an interface curated by the compiler and runtime, not core library :(

Most of this exists today. The only
real thing that changes is that we take *away* the tricks the compiler
does for `+=`.

Not ok with the thrust of this. I mean, in terms of the API for dvec or stack or whatever, that may be the right thing; the current += rule actually requires a _companion_ compiler-written optimization in the construction of single-element vectors on the RHS in order to append them w/o allocation, in a type-symmetric way, which is totally over the top and should go. So for now pushing a single element should probably, yes, involve a .push method. At least until (or if) we address the type-level question of doing operator overloading on mixed types.

But this general move feels like obscuring a pretty important bug, that I'm inclined to point out: we need to expand operator overloading to have op= forms, in general, such that a user could _define_ a version of += that does what the compiler currently does, when that's reasonable (overload arg-type symmetry aside). Operator overloading is simply incomplete here; likewise, fn [](...) needs to have a 3-arg lval form, not try to "return a mutable ref" or something.

Stacks are by no means the only case in which there's a "fast" in-place op= form that users _will_ want to write. Pretty much any nontrivial data structure with operators on it has an "in-place" optimization opportunity for several operations. There's no reason to deny them the opportunity when it appears in a for appropriate for overloaded operators; the operator is already named, reserved, parsed at right precedence, etc. It just needs to dispatch like the others.

-Graydon
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to