Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Internal moves make no sense. You can't move an object out of a container that has invariants, like an ordered map. This is why it only makes sense for the caller to move a container in to the function. This is efficient. I've edited my 3rd version appropriately, as moving from a const& was clearly bogus.

    template <typename Range>
    void draw_all (Range r) {
       for (e const& : r) { draw(e); }
    }


Using the container's iterator to move allows the container to enforce those invariants.


I've totally lost track of what we are arguing about. Anything containing a unique element has to be logically unique itself, right?

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.


> * It restricted the type of range/container passed to it

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.


Why is it a problem to "require further borrows which are only implied"? Borrows are compile-time only, and they conceptually happen anyway in your C++ implementation. It's just enforcing the correct ownership pattern in the compiler.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: