Hacker News new | ask | show | jobs
by vitus 1422 days ago
I feel like a better comparison would be to use std::span (C++20) to mimic Rust's slice. Otherwise you might be tricked into thinking that adding

    // hey don't pass in a temporary
    auto make_appender(std::vector<int>&& suffix) = delete;
(which turns the provided code into a compiler error under both g++ and clang++) is adequate to prevent the immediate class of issues (namely, C++ allows const Foo& and Foo&& to bind to temporaries).

Meanwhile, it's really really easy to make std::span dangle:

    #include <cassert>
    #include <span>
    #include <utility>
    #include <vector>
    
    std::vector<int> append(std::vector<int>&& items, std::span<int> suffix) {
      items.insert(items.end(), suffix.begin(), suffix.end());
      return items;
    }
    
    auto make_appender(std::span<int> suffix) {
      return [=](std::vector<int>&& items) {
        return append(std::move(items), suffix);
      };
    }
    
    auto make_appender34() {
      std::vector<int> vec = {3, 4};
      return make_appender(vec);
    }
    
    int main() {
      auto append34 = make_appender34();
      assert((std::vector<int>{1, 2, 3, 4} == append34({1, 2})));
    }
1 comments

That would just be another tendentious example. Nobody would write a make_appender that takes a span argument, because it makes no sense.

The point we should take away is that is actually hard to invent plausible examples of the failure that we are being told Rust would prevent.

> Nobody would write a make_appender that takes a span argument, because it makes no sense.

I don't agree with that. If you can guarantee that the data pointed to by the span will outlive your appender, then it's safe. And if you don't actually want to transfer ownership or incur the overhead of a copy, and you don't care if your input is a vector or an array, then it's the correct abstraction.

Replace std::span with std::weak_ptr (or a raw pointer), and replace the closure with a class (e.g. a tree where each node has a weak pointer to its parent), and tell me again that nobody would ever write that code. It's fundamentally the same concept: if your ownership model isn't ironclad, or if any of your assumptions are ever violated, then you can run into use-after-free.