News for hackers. Stuff that matters.
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by cherryteastain on Sunday May 05, 2024 @06:21AM (#40263676)
What's the point of adding Option<T>, Result<T,E> and Rc/Arc when std::optional, std::expected and std::shared_ptr exist?
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by tialaramex on Sunday May 05, 2024 @07:06AM (#40263925)
std::optional is a poor shadow of Option. It's what happens when C++ programmers who've seen a Maybe type in a window (years ago by the way, this isn't inspired by Rust, it was just stuck in the standardization process until C++ 17) but are starved of proper types and basic features like pattern matching try to imitate what they saw.

As a result for example std::optional<&T> doesn't exist, because to a C++ programmer it seems as though this might have assign-through semantics (!) and so WG21 decided to kick this can down the road. C++ 26 might get std::optional<&T>

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by Calavar on Sunday May 05, 2024 @07:53AM (#40264157)
The lack of support for optional<T&> is not an issue at all in my opinion. The actual issue is that std::optional is not a monadic type in the vein of Rust's Option or Haskell's Maybe. So really, what does it buy you over std::pair<T, bool>? Except being unsafe by default since it allows you to access an unconstructed T. Basic monadic operations don't arrive for std::optional until C++23, which is an unforced error. They should have been there from the beginning.
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by AHTERIX5000 on Sunday May 05, 2024 @08:15AM (#40264274)
Funny how this just keeps happening in the C++ world. I've seen ten different promise/task frameworks successfully used in production with neat APIs but somehow std::future is still just a toy. Even std::expected was released without the usual map/then.
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by nly on Sunday May 05, 2024 @07:39AM (#40264088)
I think you mean optional<T&>. Not sure I'll be taking opinions on C++ who doesn't know the basic syntax.
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by fsloth on Sunday May 05, 2024 @07:55AM (#40264169)
Are you serious :D? The arbitrary syntax toenail clippings that is C++ should be handled by IDE & compiler, linters and static type checker. C++ syntax is not music or maths, it’s just more or less arbitrary gobbledygook. (I write C++ as my main job). If you can intuitively parse all that then all the power to you but it absolutely is not requirement to do proper software engineering in C++ or have opinions on the language :).
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by OtomotO on Sunday May 05, 2024 @07:45AM (#40264113)
Syntax, Schmyntax... I learned too many languages over the years and can't remember every syntax from the top of my head, but does it really matter?
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by nly on Sunday May 05, 2024 @08:10AM (#40264241)
Yeah it matters given how the whole argument falls on advanced aspects of the type system and templates. How can you speak with authority about a language you clearly aren't using day to day?

I have never once missed the distance of an optional<T&>. The practical use of optional<> is that you know where T is going to be constructed and can reason about the memory layout. Using it as an alternative to T* has never even occurred to me. The ideal of bundling a presence flag and a pointer together (which would be the default underlying representation unless it was specialised to hold a T* internally) is gross and inefficient

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by blegr on Sunday May 05, 2024 @08:32AM (#40264369)
> The practical use of optional<> is that you know where T is going to be constructed and can reason about the memory layout.

The practical use for me is making interfaces safer. Where I saw colleagues use pointers as optionals, end up mis-tracking what can be null and what can't, only checking it inconsistently, and triggering UB, I now have a clear distinction between optional and non-optional arguments/returns with an easy way to access the contained .value() without risk of UB. The type also tells me when I should handle the empty case and when I shouldn't.

Most of the time, I want to pass/return a reference and the lack of `optional<&T>` makes it tiring. If only `std::reference_wrapper` had a shorter name, I could at least use that. But then I'd end up with `arg.value().get().attr` when `.value().attr` should be enough...

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by frutiger on Sunday May 05, 2024 @08:53AM (#40264511)
> Most of the time, I want to pass/return a reference

Surprised to hear that you want to return a reference so frequently.

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by umanwizard on Sunday May 05, 2024 @12:33PM (#40266098)
How else would you implement C++’s vector::operator[] for example?

This to me is the clearest example of something that’s safe in Rust, and impossible to make safe in C++.

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by frutiger on Sunday May 05, 2024 @12:41PM (#40266160)
> How else would you implement C++’s vector::operator[] for example?

Are you asking in a theoretical world where it isn’t defined to already return a `T&`?

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by blegr on Sunday May 05, 2024 @12:59PM (#40266318)
Now that GP said it, it'd be nice to be able to write "some_map[i].value_or(some_value)".
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by umanwizard on Sunday May 05, 2024 @01:21PM (#40266499)
You can do this in rust with Vec::get, which returns an Option<&T>.
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by umanwizard on Sunday May 05, 2024 @01:20PM (#40266494)
No, that’s my point. It returns a reference. So it’s a good example of when you might want to return a reference, which you said seemed uncommon. But the reference it returns is unsafe (for example, it gets invalidated if the vector is later resized), whereas the reference returned by the corresponding Rust operator is safe.
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by frutiger on Sunday May 05, 2024 @01:34PM (#40266598)
Sure, but the person I was relying to said they were writing code that returns references (not implementing the standard library). And my surprise was that they have to do it so often.

My overall advice here would be that if you find yourself implementing vector-like data structures very often, then it is time to take a second look the design.

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by umanwizard on Sunday May 05, 2024 @01:55PM (#40266771)
Fair enough, but even if you’re not writing code like this, you still have to use it, so you’re still exposed to the unsafety.
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by mathstuf on Sunday May 05, 2024 @09:14AM (#40264658)
These kinds of discussions remind me that not everyone codes in the same domain where the same patterns dominate. I think everyone would do well to avoid "but I don't need it, so it seems unnecessary" kinds of arguments and instead have the imagination that others may code in different domains where different patterns dominate.

Me? References get returned all the time because you want to access some state store's vector of things without copying the vector just to ask "are any of the elements X?" or adding a new method for every `std::algorithm` method for each member you might want to use it on.

The benefit of `optional<T&>` over `T*` in an API is that the former communicates "you have write access to this thing which may not exist" whereas the second needs documentation for whether `nullptr` is a thing and whether the caller needs to `delete` it (or was it `delete[]` this time?).

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by frutiger on Sunday May 05, 2024 @12:38PM (#40266134)
> I think everyone would do well to avoid "but I don't need it, so it seems unnecessary" kinds of arguments

This is an uncharitable characterisation of what I said.

> References get returned all the time because you want to access some state store's vector of things without copying the vector just to ask "are any of the elements X?"

This is what const references are for. Returning an optional<vector<T>&> to query if it contains an element would not be appropriate.

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by blegr on Sunday May 05, 2024 @11:01AM (#40265383)
It's not really my choice, I'm working in a big codebase I didn't write. Lots of large classes containing large collections with getters and setters.
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by frutiger on Sunday May 05, 2024 @12:43PM (#40266180)
Ah, that’s unfortunate.
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by tialaramex on Sunday May 05, 2024 @10:03AM (#40264981)
> The ideal of bundling a presence flag and a pointer together (which would be the default underlying representation unless it was specialised to hold a T* internally) is gross and inefficient

That's a defect in C++ rather than some principled objection though. Rust's Option isn't specialized, the Guaranteed Niche Optimisation kicks in exactly the same for &T as for most C-style enumerations, OwnedFd, NonZeroU8 or indeed my BalancedI8, this is one of those places where an engineer can see how to design the core language properly to deliver the same performance despite better ergonomics for everybody, not add a special hack.

In practice since C++ can't do that, the likely C++ 26 std::optional<T&> will be a specialization which just has a pointer inside it. This may mean lots of awkward word smithing to require that implementation or they may just trust that all the implementers will Do The Right Thing™, as with the Niebloids.

I try not to "speak with authority" about C++ because I don't think anybody has the necessary understanding to do so, including the people who wrote the ISO document, the compiler engineers, and Bjarne himself.

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by harry8 on Sunday May 05, 2024 @07:51AM (#40264141)
In 2 sentences this comment encapsulates everything wrong with C++ culture that has caused so many terrible errors becoming standardized forever over the years.

Sometimes being nice really pays off.

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by nly on Sunday May 05, 2024 @08:05AM (#40264216)
Sorry, but I just get sick of people pontificating about the academics of type systems and monadics. Bored me to death.

std::optional<> is an extremely useful and welcome addition to C++ that improves code quality, is easy to understand, and has easy to reason about code generation.

No doubt there is something that could be demonstrated in Rust with Option that is compelling, but I'd rather people showed that so a basis for comparison with modern C++ code can be made.

I suspect in practice these arguments make little difference to real world code.

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by kaashif on Sunday May 05, 2024 @09:43AM (#40264849)
The people criticizing std::optional are doing a very poor job.

Here's the big issue: unchecked access to std::optional with operator* has undefined behavior when there's no value.

This is unforgivably bad design since you can enforce exhaustive checking at compile time, but C++ isn't going in that direction.

std::optional offers value() for checked access too, but that checks at runtime and throws an exception.

It is possible to have an optional with no runtime check, no undefined behaviour, and guaranteed handling of both cases by checking at compile time. This is what Rust offers.

Undefined behaviour being easy to invoke absolutely does matter for real world code.

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by macgyverismo on Sunday May 05, 2024 @10:24AM (#40265129)
can you show me how rust does this? I'm genuinely curious. I've made a toy example to show how c++ checks for undefined behavior at compile time, I am unaware of rust being able to do the same without runtime costs (however small they may be, this is a toy example after all) https://godbolt.org/z/cT9bqz8z7
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by kaashif on Sunday May 05, 2024 @12:13PM (#40265933)
The point is that Option in Rust doesn't have undefined behavior in any case, even if the values aren't known at compile time. Exhaustiveness is always checked at compile time, unlike C++ where operator* offers an escape hatch where nothing is checked in non-constexpr contexts.

"Make everything constexpr" isn't a real solution to UB, in the same way that "make all functions pure" isn't a solution for managing side effects.

Not adding UB to your APIs, on the other hand, is a real solution.

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by umanwizard on Sunday May 05, 2024 @12:30PM (#40266073)
You can actually implement the C++ behavior, if you want:

    unsafe fn super_unwrap<T>(x: Option<T>) -> T {
        match x {
            Some(val) => val,
            None => unreachable_unchecked!(),
        }
    }

But defaults matter, and Rust certainly doesn’t make this kind of thing ergonomic (which is a correct decision on the Rust designers’ part).
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by kaashif on Sunday May 05, 2024 @02:35PM (#40267096)
Yeah, absolutely. My point is that Option itself doesn't give you this API and to make an unsafe version, you have to explicitly write it.

Including UB in easy to misuse places is totally unnecessary and a footgun which really does cause issues in real code.

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by umanwizard on Sunday May 05, 2024 @05:49PM (#40268798)
Yep, I agree completely. Just wanted to point out for completeness that rust can theoretically do the same thing as c++.
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by tialaramex on Sunday May 05, 2024 @08:21PM (#40269893)
You don't have to write this, it already exists as the (unsafe of course) method Option::unwrap_unchecked

Because all Rust's methods can be called as free functions, you can literally write Option::unwrap_unchecked for the same behaviour, or you can some_option.unwrap_unchecked() (in both cases you will need to be in unsafe context for this to be allowed and should write a SAFETY comment explaining why you're sure it's correct)

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by umanwizard on Monday May 06, 2024 @08:40AM (#40273934)
I see. I didn't know that method existed despite spending ~4.5 years writing Rust.
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by kaashif on Thursday May 09, 2024 @01:00PM (#40310273)
Ha, same. I very very rarely write code in unsafe contexts which is why, I guess.
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by elteto on Sunday May 05, 2024 @10:36AM (#40265196)
Compile time checked pattern matching: https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by macgyverismo on Sunday May 05, 2024 @11:03AM (#40265404)
That matches the 'static_assert' portion of my sample code. The implied claim of the parent I replied to was that rust could do this even for runtime values, such as the one I am using in the main of my sample. In c++ it is the same function running both the compile time check and the unchecked runtime variant, so there is zero overhead at runtime. I can't possibly think of a way how rust would be able to make the same code in my sample safe without adding runtime checks. If I am mistaken here I sure would like to know.
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by umanwizard on Sunday May 05, 2024 @12:32PM (#40266088)
You’re correct. Rust can’t statically prove which enum variant is inhabited. You do need a runtime switch, the difference is (at least in safe code) it statically forces you to indeed do that runtime switch.
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by kaashif on Sunday May 05, 2024 @02:33PM (#40267084)
You aren't mistaken. I should've written "runtime overhead" - my point is that there is no runtime performance penalty for getting rid of the UB in the Option API.

An equivalent API with no UB is just strictly better.

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by triblemaster on Sunday May 05, 2024 @07:41AM (#40264096)
You might now want to, but the opinion is correct.
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by mgaunard on Sunday May 05, 2024 @10:05AM (#40264996)
std optional is based on boost optional which was written in 2003 before any sort of lambdas made monadic operations usable.

The main concern with that component was ensuring we can allocate stack storage for an object that may or may not be initialized.

The reference is easily achievable by using T* so is of minimal value, but also poses some more semantic problems since a reference is not copyable while an optional is.

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by tialaramex on Sunday May 05, 2024 @09:28PM (#40270273)
I actually don't care that much about the monadic functions.

For me the important use case is pattern matching, which C++ doesn't yet have. Pattern matching really changes how you see the entire language.

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by mgaunard on Monday May 06, 2024 @12:14AM (#40271087)
C++ has pattern matching through overloading.
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by tialaramex on Tuesday May 07, 2024 @07:29PM (#40292704)
How do you figure?
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by mgaunard on Wednesday May 08, 2024 @03:15AM (#40295208)
I don't understand the question.

Here is the first example I found on Google if that helps you understand.

    std::variant<Fluid, LightItem, HeavyItem, FragileItem> package;

    std::visit(overload{
        [](Fluid& )       { std::cout << "fluid\n"; },
        [](LightItem& )   { std::cout << "light item\n"; },
        [](HeavyItem& )   { std::cout << "heavy item\n"; },
        [](FragileItem& ) { std::cout << "fragile\n"; }
    }, package);
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by tialaramex on Thursday May 09, 2024 @10:35AM (#40308651)
But that's not really even a pattern match? Here's what a pattern match looks like: [This is from day 10 of last year's Advent of Code.]

            match (state, pipe) {
                (State::None, Pipe::Ground) => {
                    if inside {
                        n += 1;
                    }
                }
                (State::None, Pipe::Vert) => {
                    inside = !inside;
                }
                (State::None, Pipe::Se) => {
                    state = State::South;
                }
                (State::None, Pipe::Ne) => {
                    state = State::North;
                }

                // Horizontal lines make no difference to anything
                (State::North | State::South, Pipe::Horiz) => {}

                // U-turns
                (State::South, Pipe::Sw) | (State::North, Pipe::Nw) => {
                    state = State::None;
                }

                // Form a vertical line
                (State::South, Pipe::Nw) | (State::North, Pipe::Sw) => {
                    inside = !inside;
                    state = State::None;
                }

                _ => {
                    panic!("Unexpected sequence {state:?} {pipe:?}");
                }
            }
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by mgaunard on Friday May 10, 2024 @05:15AM (#40316863)
This is the exact same thing except you're visiting two arguments at a time.

Guess what, the same syntax I gave supports exactly that as well.

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by meindnoch on Tuesday May 07, 2024 @04:35AM (#40283512)
Easy. When you want std::optional<T&> just use T*.
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by jeroenhd on Sunday May 05, 2024 @06:43AM (#40263792)
The Option type seems to have various standard Rust methods like expect() implemented that I don't believe std::optional has.

I haven't checked recent C++ standards, but I don't believe you can use partial classes/extensions in C++ like some other OO languages to add these methods to a native type. Many helper functions commonly used in Rust also only seem to exist in C++23, which not ever project can be compiled under yet.

In normal C++ code, the native types would probably be better to use, but if you're going full Rust style code, you may as well use these new types.

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by blegr on Sunday May 05, 2024 @08:36AM (#40264397)
> The Option type seems to have various standard Rust methods like expect()

Isn't that value()?

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by jeroenhd on Sunday May 05, 2024 @11:02AM (#40265389)
.expect() also takes a message that it prints when the value is empty.

Unlike C++, Rust doesn't support throwing exceptions, so expect() failing would panic. By default, this means dumping a stack trace and terminating the program, and the message provided in "expect" would be printed right before the stack trace.

For example:

    fn main() {
        let x: Option<i32> = None;
        x.expect("Oh no!");
    }

will print:

    thread 'main' panicked at src/main.rs:3:7:
    Oh no!
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
With RUST_BACKTRACE set to "full", it'll print:

    $ RUST_BACKTRACE=full ./target/release/demo
    thread 'main' panicked at src/main.rs:3:7:
    Oh no!
    stack backtrace:
       0:     0x5ff43befa755 - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::ha52e99bffe3c0898
       1:     0x5ff43bf1769b - core::fmt::write::h5fdd5156f2480a24
       2:     0x5ff43bef8a5f - std::io::Write::write_fmt::ha2c0b019f448d2c3
       3:     0x5ff43befa52e - std::sys_common::backtrace::print::he84813a4ed1c2825
       4:     0x5ff43befb7e9 - std::panicking::default_hook::{{closure}}::h033521c27c9929b1
       5:     0x5ff43befb52d - std::panicking::default_hook::had42987aad9de78c
       6:     0x5ff43befbc83 - std::panicking::rust_panic_with_hook::h80fc1b429f5a5699
       7:     0x5ff43befbb64 - std::panicking::begin_panic_handler::{{closure}}::h5aa7b89233b1ae33
       8:     0x5ff43befac19 - std::sys_common::backtrace::__rust_end_short_backtrace::h0e4c5e6cee7f8a24
       9:     0x5ff43befb897 - rust_begin_unwind
      10:     0x5ff43bee0b63 - core::panicking::panic_fmt::h3bea7be9b6a41ace
      11:     0x5ff43bf16c6c - core::panicking::panic_display::h20da06138ce63f85
      12:     0x5ff43bee0b2c - core::option::expect_failed::h92448d4f1092eaaa
      13:     0x5ff43bee127a - demo::main::ha244b8f1ce6eaa44
      14:     0x5ff43bee1223 -     std::sys_common::backtrace::__rust_begin_short_backtrace::hfc5c93265480da58
      15:     0x5ff43bee1239 - std::rt::lang_start::{{closure}}::h988fdfb65ef3da3b
      16:     0x5ff43bef6be6 - std::rt::lang_start_internal::h64c4082ce77a6bd6
      17:     0x5ff43bee12a5 - main
      18:     0x741d2a628150 - __libc_start_call_main
                               at ./csu/../sysdeps/nptl/libc_start_call_main.h:    58:16
      19:     0x741d2a628209 - __libc_start_main_impl
                                   at ./csu/../csu/libc-start.c:360:3
      20:     0x5ff43bee1155 - _start
      21:                0x0 - <unknown>
Whether you would want to replicate this behaviour in C++, I don't know; I find panic!() to be quite destructive, and catastrophic when it's used in libraries or frameworks. I think the C++ implementation just throws an exception, but Rust's .expect() does not behave like .value() in C++.
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by tialaramex on Sunday May 05, 2024 @10:12AM (#40265032)
pub fn expect(self, msg: &str) -> T

So that says it's a method (its first parameter is the type itself, but named self rather than as a normal parameter so we can use method syntax instead of calling the function Option::expect) but it also takes an immutable reference to a string slice.

That second parameter, msg, is the text for a diagnostic if/ when you're wrong.

So, in a sense it's like value() but the diagnostic text means, when I was wrong...

  let goose = found.expect("Our goose finder should always find a goose");
... I get a diagnostic saying that the problem is with "Our goose finder should always find a goose". Huh. I think we know where to start trouble shooting.
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by blegr on Sunday May 05, 2024 @11:07AM (#40265433)
Right, but that's redundant with the stack trace. It's not actually helpful to run a big program I don't know very well and panic with a single "your goose isn't cromulent!" message from a call 20 levels deep.

In your example, it's likely that the person who sees this message won't have enough context to understand it; it's more like a debugging assert. Since you'll need a debugger and a breakpoint anyway, the message isn't very helpful.

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by filleduchaos on Sunday May 05, 2024 @12:19PM (#40265996)
Maybe it's just me but a note from the developer stating why it's important that some particular value be present is exactly the sort of help I would like when looking at a call stack that's dozens of levels deep. Especially considering that a panic terminates execution - I very much would like to know what was so critical that the program had to preemptively crash up front and not after pawing through code and docs.

I think it's pretty odd to use a quick example someone rattled off on a web forum to explain a function's behaviour as evidence of its usefulness or lack thereof, as if the only thing a person could possibly write in a freeform error message is "Our goose finder should always find a goose".

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by blegr on Sunday May 05, 2024 @12:31PM (#40266084)
I see your point, but my experience is that you need the stack trace first, and the developer’s explanation second. Asserts crashing with a message that makes perfect sense in its context but is completely useless for debugging are the bane of my workweek.

Now I appreciate a clear explanation for an uncommon assert and for example, OpenCV could do with more of those, but in most functions, seeing the line that throws the error is enough to understand.

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by tialaramex on Sunday May 05, 2024 @03:15PM (#40267371)
The nature of expect is that this is a bug. The person who wrote this code was wrong, they expected that this optional has Some value but it does not.

In most cases then, if you don't know this code very well, that's fine because it's not your bug. In the edge case that you just got handed a pile of poorly documented code somebody else wrote, perhaps over several years, well, at least you know what they thought is supposed to happen here and that they're wrong.

And no, I don't find it better to be told "It broke, break out a debugger and try to reproduce the fault". With this text we can revisit the Goose wrangling code and maybe, now that we're staring at it knowing a real customer saw this fault, we are inspired and realise that sometimes it won't find a Goose, then decide what to do about that.

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by vips7L on Sunday May 05, 2024 @10:28AM (#40265150)
Are there no stack traces? Wouldn’t that point you to where to start trouble shooting?
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by tialaramex on Sunday May 05, 2024 @03:02PM (#40267290)
No, you are not guaranteed a stack trace, in an optimised release build it may not even be possible to construct a valid trace. If you can reproduce the problem you can say you want this run to have a stack trace, but if your release builds just exit immediately on panic then there's no reason for them to be able to provide a stack trace of the fault.

On the other hand expect will provoke the message you wrote if it fails. Of course if it's inside a consumer's fitness tracker it probably doesn't have any way to show the message to a human, but that's a different problem - the fitness tracker presumably can't display stack traces either.

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by n_plus_1_acc on Sunday May 05, 2024 @06:50AM (#40263831)
std::optional<T> is fundamentally broken because it has an imolicit conversion (or operator* or something) to T. If you forget to check if it's empty you get UB.
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by lorenzhs on Sunday May 05, 2024 @07:20AM (#40263996)
There is no implicit conversion (except to bool, but that tells you whether the optional contains a value), and operator* / operator-> throw std::bad_optional_access if it’s empty. See https://en.cppreference.com/w/cpp/utility/optional
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by tialaramex on Sunday May 05, 2024 @07:30AM (#40264037)
You're describing what it would do in a sane world where WG21 cared about safety.

In this world, as the document you've linked says: "The behavior is undefined if *this does not contain a value."

The operators for such access are actually `noexcept` - the exception you're apparently relying on would be illegal.

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by kzrdude on Sunday May 05, 2024 @07:40AM (#40264094)
Can we salvage this by forbidding * on optional with compiler warnings (as errors)?
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by lorenzhs on Monday May 06, 2024 @07:06AM (#40273265)
clang-tidy has a check for this -- it's not a compiler check but with clangd and LSP, almost every code editor can show an inline warning: https://clang.llvm.org/extra/clang-tidy/checks/bugprone/unch...
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by lorenzhs on Sunday May 05, 2024 @03:18PM (#40267395)
Should’ve checked my own link instead of relying on memory — I might have some code to revisit on Monday. That’s insane, thanks for correcting me!
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by tialaramex on Sunday May 05, 2024 @09:02PM (#40270133)
No problem, if I caused you to fix a bug before it happened that's great. Yes, I find that reading sources I'm about to cite is often eye-opening. Our memories are not as good as we think they are, and our condensed understanding of a complex situation may have ignored something which is now crucial.

Once in a while I go down a rabbit hole, but hey, it's not as though HN isn't a rabbit hole anyway.

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by masklinn on Sunday May 05, 2024 @07:25AM (#40264013)
> and operator* / operator-> throw std::bad_optional_access if it’s empty.

Of course not, they’re literally `noexcept`, what they do is UB if empty.

value() will throw.

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by formerly_proven on Sunday May 05, 2024 @08:30AM (#40264347)
Step 1 of API design: Always make the easiest and shortest way the wrong way.
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by steveklabnik on Sunday May 05, 2024 @10:00AM (#40264956)
They were really in a pickle here. It’s easy to be snarky, but both options (no pun intended) have downsides. In short, do you choose consistency by default, or safety by default?

This feels like an easy choice in isolation, but at the time this was being developed (and arguably even now), there’s no definitive plan to holistically move C++ code to being safe by default. So whenever that happens, a ton of things will need to be dealt with, and there’s always the possibility that being an odd API here makes that overall move harder not easier. And C++ is regularly criticized for being inconsistent. Do you deepen those criticisms just so that one tiny corner of an API is better?

If I’m honest with myself, I probably would have made the same choices they did in this situation.

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by tialaramex on Sunday May 05, 2024 @10:26AM (#40265138)
> If I’m honest with myself, I probably would have made the same choices they did in this situation.

Some of the more modern proposals (std::optional is quite old) actually make an explicit appeal to WG21 not to choose consistency at the price of safety because it just needlessly makes the language worse. "But we made the language worse before" is more like a plea for help than an excuse.

Barry Revzin did this in his "do expressions" which are an attempt to kludge compound expressions into C++ which really wants them to be compound statements instead. For consistency, all the obvious mistakes you'll make in do expressions could introduce UB like they would in equivalent C++ core features, but Barry argues they should be Ill-Formed instead - resulting in your mistakes not compiling rather than having undefined behaviour.

Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by masklinn on Sunday May 05, 2024 @09:00AM (#40264569)
C++ APIs follow the principle of most astonishement.
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by blegr on Sunday May 05, 2024 @08:38AM (#40264407)
It sucks but it's easy to review and avoid, probably could be checked statically by linters too.
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by lorenzhs on Monday May 06, 2024 @07:07AM (#40273269)
Re:Rusty.hpp: A Borrow Checker and Memory Ownership System f... by SilasX on Sunday May 05, 2024 @07:36AM (#40264075)
Love the typo, and it's fitting here. I'm going to use it for any time the implicit behavior risks burning (immolating) you, as the sibling comments note applies std::optional.

Implicit conversion that immolates: imolicit conversion.