Maybe Nimrod?
Maybe Nimrod?
[Edit] Read Nimrod tutorials and ported a few toy programs. It's encouragingly clean and doesn't seem to shy away from features... I wonder if Alex Stepanov knows that, in Nimrod, "if you overload the == operator, the != operator is available automatically and does the right thing" ;)
I'm still using C++ myself until the dust settles a bit, but I've found that my C++(11) code is trending towards being less stateful and more functionl-ish.
It's cool to see things changing in this space after feeling like it would be old style C or C++ forever.
Consider this from the tutorial:
fn draw_all(shapes: &[~Drawable]) {
vs C++: draw_all (const vector<unique_ptr<const Drawable>>& shapes) {
Transcribing that took a few seconds of thought, and all the terseness of a few symbols has accomplished is to obscure a poor choice of API and ownership semantics. That's just one symbol... how horrific can we make something without noticing with just a few more params and symbols?In some instances Rust has removed syntax or its prevalence (@ turned into Gc/Rc; ~[] no longer grows, but Vec does).
To each their own.
draw_all (vector<unique_ptr<Drawable>> const& shapes) {
Now the const and ref at together.(Also, your equivalence is not quite correct: `&[T]` is more like a Boost range. In particular your transcription makes it look like the original code only accepted Vec<T>, which is not the case.)
> That's just one symbol... how horrific can we make something without noticing with just a few more params and symbols?
You've pretty much covered all the type-level symbols in Rust, except for * for unsafe pointers.
Also, doesn't having a borrowed array of unique references to Drawables mean the elements of the array are either now implicitly borrowed, or I have to borrow each of them before they're accessed? Just knowing the symbols don't make the semantics clear. In C++ all smart pointers are values in their own right. In my example I have a reference to an array of smart pointers, and there's no magic.
`&[T]` isn't generic: it's a bounds-checked slice. Two pointers: start and end.
Presumably `Drawable` is in the signature so that methods specific to `Drawable` can be called.
> Also, doesn't having a borrowed array of unique references to Drawables mean the elements of the array are either now implicitly borrowed, or I have to borrow each of them before they're accessed?
They work like anything else: if you want to take a reference to a Drawable, you borrow it.
> Just knowing the symbols don't make the semantics clear.
Yes. Also true for C++'s symbols; e.g. `&`.
> In C++ all smart pointers are values in their own right.
Same in Rust.
> In my example I have a reference to an array of smart pointers, and there's no magic.
Same in that example.
template <typename Range>
void draw_all (Range r) {
for (e : r) { draw(e); }
}
There are no magical symbols here at all. It's not efficient, but it's completely memory safe... breaks with unique_ptr though, which is a good indication for me that unique_ptr is the wrong choice. Here's the less safe 'borrowing' version: template <typename Range>
void draw_all (Range const& r) {
for (e const& : r) { draw(e); }
}
and a sane compromise: template <typename Range>
void draw_all (Range r) {
for (e const& : r) { draw(e); }
}
How would you write all of these in Rust?You could come up with a generic function that doesn't require unique ownership (for example, one that takes an Iterator<&Drawable>), but the function in that example wasn't generic because making everything generic just in case is overengineering.
You seem pretty confused about how borrowing works. Borrowing is tangential to that function.
Anyway, if you wanted a generic reference-taking version:
fn draw_all<I:Iterator<&Drawable>>(iterator: I) {
for drawable in iterator {
drawable.draw();
}
}
And a generic move version: fn draw_all<I:Iterator<Drawable>>(iterator: I) {
for drawable in iterator {
drawable.draw();
}
} template <typename Range>
void draw_all (Range r) {
for (e const& : r) { draw(e); }
}In C++, which defaults to value semantics, it's required that you move your container if it contains a non-copyable (unique) element. So you only need to move into the draw_all function in this case, which is why taking the range by value is not just efficient, but semantically correct. If the caller moves in to the function, then when it returns the caller will no longer own any elements. The callers vector will be empty, and the elements themselves will still be unique having never been copied, moved or "borrowed".
If borrowing isn't a performance hack, then why not make everything you're ever likely to borrow shared? I'd argue anything you're drawing is shared between the draw routine and the caller. Drawing a distinction just because the caller is suspended, seems like an impediment to future change if, for example, you later switch to a coroutine or an asynchronous/threaded operation. Copying the range and sharing elements gets you this for free.
In summary, 'draw_all' as specified was a bad API because:
* It restricted the type of range/container passed to it
* It had unnatural ownership semantics (borrowing a box of unique things without saying you're borrowing those things is weird).
* The implementation, as was, required further borrows which were only implied. In C++ you take everything straight away.
Yes, it did, but as I mentioned before, it's overengineering to make everything generic that could possibly be generic.
> * It had unnatural ownership semantics (borrowing a box of unique things without saying you're borrowing those things is weird).
No, it's not, it's quite natural. `&[&Drawable]` is not a subtype of `&[~Drawable]`, so if your caller has an array of `&[~Drawable]`, then they would have to recreate the array to pass it to that function.
> * The implementation, as was, required further borrows which were only implied. In C++ you take everything straight away.
I don't understand what this means, but in any case C++ and Rust don't differ substantially on ownership/reference/move semantics.
[0] https://github.com/eudoxia0/cmacro/blob/master/grammar/lexin...