Live data from Hacker News

Rusty.hpp: A Borrow Checker and Memory Ownership System for C++20

github.com

151–160 of 174 posts

Re: Rusty.hpp: A Borrow Checker and Memory Ownership System for C++20

#151

Earlier quoted context omitted.

I read that more as a valid criticism of other parts of C++ rather than about smart pointers as a way to track ownership. e.g. std::string_view seems broken by design in wanting to support both raw-pointer based strings with zero ownership semantics as well as std::string. A string view (abstract concept) really needs to either have shared ownership of the underlying string, or have a non-owning reference that knows…

Well the string view type you wish existed seems to be exactly what Rust gives you, no? Non-owning references that "know when they have been invalidated" (or rather, the compiler prevents you from using them after they have been invalidated). I'm not sure why this means you shouldn't be able to create a string_view on top of std::string, though. You can create a Rust &str on top of String, it just doesn't participate…

My comment was just a reply to the parent - that the linked article wasn't really about smart pointers. I was just using string_view as an example.

There are lot's of places where C++'s long history shows it's ragged edges - where newer features really don't play so nice with older ones. One would certainly hope that a new language like Rust is at least initially more consistent.. the question is what will it look like in 20 years time, if it's still being actively developed at that time?

Re: Rusty.hpp: A Borrow Checker and Memory Ownership System for C++20

#152

Earlier quoted context omitted.

Well the string view type you wish existed seems to be exactly what Rust gives you, no? Non-owning references that "know when they have been invalidated" (or rather, the compiler prevents you from using them after they have been invalidated). I'm not sure why this means you shouldn't be able to create a string_view on top of std::string, though. You can create a Rust &str on top of String, it just doesn't participate…

My comment was just a reply to the parent - that the linked article wasn't really about smart pointers. I was just using string_view as an example. There are lot's of places where C++'s long history shows it's ragged edges - where newer features really don't play so nice with older ones. One would certainly hope that a new language like Rust is at least initially more consistent.. the question is what will it look li…

Rust's &str is basically identical to C++'s string_view, for what it's worth. I still don't understand your point about how string_view is inconsistent. The only reason &str is so much easier to use than string_view is because Rust supports borrow checking, making it safe to use, whereas C++ does not.

Re: Rusty.hpp: A Borrow Checker and Memory Ownership System for C++20

#153
post #67

Can someone familiar with both please explain the benefit of Rust's borrow checker memory management model over C++'s std::unique_ptr and shared_ptr ? Is there some safety argument to prefer Rust's model, or is it something else ? I'm not aware of any C++ compiler doing it, but it seems smart pointer overhead could be automatically and safely reduced (in same way one can do it manually) by the compiler lowering the g…

The point is that it keeps track of multiple references and disallows mutable and immutable references at the same time across threads, for example, and disallows multiple mutable references altogether. The rust borrow checker works on values, and all that, not just on objects with RAII.

Mutable references can never coexist with other references in Rust, regardless of whether they're on different threads.

This will not compile:

    let x = 42;
    let r1 = &x;
    let m1 = &mut x;
    println!("{r1}");

Re: Rusty.hpp: A Borrow Checker and Memory Ownership System for C++20

#154

Earlier quoted context omitted.

My comment was just a reply to the parent - that the linked article wasn't really about smart pointers. I was just using string_view as an example. There are lot's of places where C++'s long history shows it's ragged edges - where newer features really don't play so nice with older ones. One would certainly hope that a new language like Rust is at least initially more consistent.. the question is what will it look li…

Rust's &str is basically identical to C++'s string_view, for what it's worth. I still don't understand your point about how string_view is inconsistent. The only reason &str is so much easier to use than string_view is because Rust supports borrow checking, making it safe to use, whereas C++ does not.

What I meant about "inconsistency" is that there are std::string_view constructors that accept raw pointers to indicate the range, and others that accept iterators. It's a mix of old (C) & new (C++) data structures, with neither indicating the ownership or longevity of the underlying object.

This is somewhat typical of where C++ is at nowadays - layering new functionality on top of old that wasn't designed to accommodate it. In an ideal world the language and libraries would be refactored and rationalized, but of course backwards compatibility precludes that. This is the fate of old languages - stay unchanged and become obsolete, or keep layering on new functionality and become messy and inconsistent.

Re: Rusty.hpp: A Borrow Checker and Memory Ownership System for C++20

#155

Earlier quoted context omitted.

> Why not? What if you have some function f(T *) that you want to call? In C++ (vs C), if the intent is to pass something large efficiently, then you'd use a reference parameter, not a pointer. You seem to be confused about the meaning of C++ smart pointers - the whole point of them (as a replacement for C's raw pointers) is that they control and indicate ownership. You can't just assign a smart pointer to something…

> In C++ (vs C), if the intent is to pass something large efficiently, then you'd use a reference parameter, not a pointer. Sure, sorry, I was using "pointer" and "reference" interchangeably. Indeed, references are pointers under the hood. > You seem to be confused about the meaning of C++ smart pointers I am not confused at all. I understand exactly what unique_ptr and shared_ptr are in C++. They are basically the e…

Interesting - so essentially calling a "non-const" (mutable) method invalidates any existing references to the object, with this being implemented at compile time by not allowing the mutable method to be called while other references are still alive ?

How exactly is this defined for something like index() which is returning a reference to a different type than the object itself, and where the declaration doesn't indicate that the referred to T is actually part of the parent object? Does the language just define that all references (of any type) returned by member functions are "invalidated" (i.e. caught by compiler borrow checker) by the mutable member call?

What happens in Rust if you attempt to use a reference to an object after the object lifetime has ended? Will that get caught at compile time too, and if so at what point (when attempt is made to use the reference, or at end of object lifetime) ?

Re: Rusty.hpp: A Borrow Checker and Memory Ownership System for C++20

#156

Earlier quoted context omitted.

> In C++ (vs C), if the intent is to pass something large efficiently, then you'd use a reference parameter, not a pointer. Sure, sorry, I was using "pointer" and "reference" interchangeably. Indeed, references are pointers under the hood. > You seem to be confused about the meaning of C++ smart pointers I am not confused at all. I understand exactly what unique_ptr and shared_ptr are in C++. They are basically the e…

Interesting - so essentially calling a "non-const" (mutable) method invalidates any existing references to the object, with this being implemented at compile time by not allowing the mutable method to be called while other references are still alive ? How exactly is this defined for something like index() which is returning a reference to a different type than the object itself, and where the declaration doesn't indi…

> Interesting - so essentially calling a "non-const" (mutable) method invalidates any existing references to the object, with this being implemented at compile time by not allowing the mutable method to be called while other references are still alive ?

Yes, exactly.

> How exactly is this defined for something like index() which is returning a reference to a different type than the object itself, and where the declaration doesn't indicate that the referred to T is actually part of the parent object?

Only if they have the same lifetime (the 'a in my example). For example, imagine a function that gets an element of a vector and uses that to index into another vector. You might write it like this:

    fn indirect_index(v1: &'a Vec, v2: &'b Vec, i: usize) -> &'b T {
        let j = v1[i];
        &v2[j]
    }
The returned value is not invalidated by any future mutations of the first vector, but only the second vector, since they share the lifetime parameter 'b.

> What happens in Rust if you attempt to use a reference to an object after the object lifetime has ended?

This is prevented at compile time by the borrow checker. E.g.:

    // this takes ownership of the vec,
    // and just lets it go out of scope 
    fn drop_vec(_v: Vec) {
    }
    
    fn main() {
        let v = vec![1, 2, 3];
        let x = &v[0];
        drop_vec(v);
        println!("{x}");
    }
This program fails to compile with the following error:

    error[E0505]: cannot move out of `v` because it is borrowed
      --> src/main.rs:9:14
       |
    7  |     let v = vec![1, 2, 3];
       |         - binding `v` declared here
    8  |     let x = &v[0];
       |              - borrow of `v` occurs here
    9  |     drop_vec(v);
       |              ^ move out of `v` occurs here
    10 |     println!("{x}");
       |               --- borrow later used here

Re: Rusty.hpp: A Borrow Checker and Memory Ownership System for C++20

#157

Earlier quoted context omitted.

> Why not? What if you have some function f(T *) that you want to call? In C++ (vs C), if the intent is to pass something large efficiently, then you'd use a reference parameter, not a pointer. You seem to be confused about the meaning of C++ smart pointers - the whole point of them (as a replacement for C's raw pointers) is that they control and indicate ownership. You can't just assign a smart pointer to something…

> In C++ (vs C), if the intent is to pass something large efficiently, then you'd use a reference parameter, not a pointer. Sure, sorry, I was using "pointer" and "reference" interchangeably. Indeed, references are pointers under the hood. > You seem to be confused about the meaning of C++ smart pointers I am not confused at all. I understand exactly what unique_ptr and shared_ptr are in C++. They are basically the e…

> Neither clang nor g++ does so, even with -Wall. I just checked. How could they?

Just by having built-in knowledge of standard library types such as std::vector, the same way the compiler has built-in knowledge of some library functions such as C's printf().

I wouldn't expect such policing to be perfect, but the compiler could at least catch simple cases where reference/iterator use follows an invalidating operation in the same function.

Don't get me wrong - I'm not defending C++. It's a beast of a language, and takes a lot of experience and self-discipline to use without creating bugs that are hard to find.

Re: Rusty.hpp: A Borrow Checker and Memory Ownership System for C++20

#158

Earlier quoted context omitted.

> Where Rust won't compile when a lifetime can't be determined, IIRC Nim's static analysis will make a copy (and tell you), so it's more as a performance optimisation than for correctness. Wait, how does that work? For example, take the following Rust function with insufficient lifetime specifiers: pub fn lt(x: &i32, y: &i32) -> &i32 { if x You're saying Nim will change one/all of those references to copies and will…

It will not emit warnings saying it did that. The static analysis is not very transparent. (If you can get the right incantation of flags working to do so and it works, let me know! The last time I did that it was quite bugged.) Writing an equivalent program is a bit weird because: 1) Nim does not distinguish between owned and borrowed types in the parameters (except wrt. lent which is bugged and only for optimizatio…

> It will not emit warnings saying it did that.

You're right. I was sure I read that it would announce when it does a copy over a sink but now I look for it I can't find it!

> The static analysis is not very transparent.

There is '--expandArc' which shows the compile time transformations performed but that's a bit more in depth.

Re: Rusty.hpp: A Borrow Checker and Memory Ownership System for C++20

#159

Earlier quoted context omitted.

> In C++ (vs C), if the intent is to pass something large efficiently, then you'd use a reference parameter, not a pointer. Sure, sorry, I was using "pointer" and "reference" interchangeably. Indeed, references are pointers under the hood. > You seem to be confused about the meaning of C++ smart pointers I am not confused at all. I understand exactly what unique_ptr and shared_ptr are in C++. They are basically the e…

> Neither clang nor g++ does so, even with -Wall. I just checked. How could they? Just by having built-in knowledge of standard library types such as std::vector, the same way the compiler has built-in knowledge of some library functions such as C's printf(). I wouldn't expect such policing to be perfect, but the compiler could at least catch simple cases where reference/iterator use follows an invalidating operation…

> I'm not defending C++.

Right, but you were asking what advantage Rust has over C++, which is what I'm trying to explain. (If you had instead asked what advantage C++ has over Rust, I'd have given a very different answer!)

> It's a beast of a language, and takes a lot of experience and self-discipline to use without creating bugs that are hard to find.

Rust makes creating a certain class of these hard-to-find bugs much harder.

Re: Rusty.hpp: A Borrow Checker and Memory Ownership System for C++20

#160
post #123

Earlier quoted context omitted.

Nim has a garbage collector. That said, you're right on some level that it's truly semantics that matter, not syntax, but you need syntax to control the semantics.

Nim is stack allocated unless you specifically mark a type as a reference, and "does not use classical GC algorithms anymore but is based on destructors and move semantics": https://nim-lang.org/docs/destructors.html Where Rust won't compile when a lifetime can't be determined, IIRC Nim's static analysis will make a copy (and tell you), so it's more as a performance optimisation than for correctness. Regardless of th…

I did not hear that Nim made ORC the default, thanks for that!

I still think that my overall point stands: sure, you can treat this as an optimization pass, but that kind of overhead isn't acceptable in the C++/Rust world. And syntax is how you communicate programmer intent, to resolve the sorts of ambiguous cases described in some other comments here.

I am again reminded of escape analysis https://steveklabnik.com/writing/borrow-checking-escape-anal...

Post reply on HN