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

That was really fun to read! Nice work digging into the root cause.

The issue where boxing State#state.partition copies the entire stage object is very counter-intuitive and would have got me as well. I would expect it to only store the partition value.



This reminds me of how Rust 2021 switched to disjoint captures in closures (https://doc.rust-lang.org/edition-guide/rust-2021/disjoint-c...). Of course in Rust, there are behavioral and compiler error differences due to mutability and the borrow/lifetime checker, beyond merely the log output of equivalent referentially-transparent objects. Honestly I still prefer the C++ way of allowing explicit lambda captures.


> Honestly I still prefer the C++ way of allowing explicit lambda captures.

That doesn’t solve overly attached partial captures in any way though?

Except possibly by driving you towards splitting the structure if you also always write full precise capture clauses?


In C++, if you copy an object by reference, it doesn't create a copy (but risks use-after-free, Rust's borrow checker is definitely a strength over C++). If you copy by value it copies (which is an unwanted hidden allocation, but in the case of Rc/Arc, I wish Rust made incref as easy as C++).

The real benefit of C++ explicit captures is making it dead simple to capture exactly the state you need, and make it explicit to the reader: [bar=obj.foo.bar]() { ... }. Rust 2021 adds selective closure capture, the downside being the exact fields captured (which affect what the lambda borrows) is implicit. To explicitly mark which fields you capture, you'd need to `let bar = obj.foo.bar; || {}` either in the scope of the lambda, or hide the lambda in a statement expression (creating a second level of indentation, risking rightward drift).

Additionally adding explicit lambda captures to Rust would alleviate its lack of copy semantics for smart pointers, allowing you to copy pointers into lambdas without polluting the parent scope or introducing a level of nesting: [ptr=Rc::clone(ptr)] || {...}. This would eliminate the need for gtk-rs's clone! macro for strong (not weak) references.


> In C++, if you copy an object by reference, it doesn't create a copy (but risks use-after-free, Rust's borrow checker is definitely a strength over C++). If you copy by value it copies (which is an unwanted hidden allocation, but in the case of Rc/Arc, I wish Rust made incref as easy as C++).

Which has nothing to do with the question.

> The real benefit of C++ explicit captures is making it dead simple to capture exactly the state you need, and make it explicit to the reader

You could just have answered that it's the latter.

> To explicitly mark which fields you capture, you'd need to `let bar = obj.foo.bar; || {}` either in the scope of the lambda, or hide the lambda in a statement expression (creating a second level of indentation, risking rightward drift).

Oh no. Anyway.

> Additionally adding explicit lambda captures to Rust would alleviate its lack of copy semantics for smart pointers, allowing you to copy pointers into lambdas without polluting the parent scope or introducing a level of nesting: [ptr=Rc::clone(ptr)] || {...}.

That's literally just a repetition of your previous sentence.

> This would eliminate the need for gtk-rs's clone! macro for strong (not weak) references.

If it would, then you already do not need it, because you can use the precise capture pattern (a block expression around a `move` closure) in order to create your clone and the clone macro is just a convenience.


> That doesn’t solve overly attached partial captures in any way though?

> Which has nothing to do with the question.

Update: now that I think about it, Rust's borrow checker (not C++) is a good warning system for undesired captures, since if you capture an object rather than a field, Rust will likely throw an error when you try mutating the captured object or returning the lambda past the lifetime of the object reference. And Rust 2021 moves to capturing struct fields by default to avoid undesired captures, but I prefer C++'s more flexible capture system (but not its absence of borrow checker) to allow concisely and ergonomically picking exactly which variables/fields/expressions to capture.

I still feel Rust's way of adding explicit lambda captures (polluting the outer scope or introducing two layers of nesting) is deeply unsatisfactory, and its approach to turning off disjoint captures (dummy `let _ = &x; x.field`, see https://doc.rust-lang.org/edition-guide/rust-2021/disjoint-c...) is also unsatisfactory. If you actually feel having to type extra and clutter the code is "good enough" for Rust and doesn't punish use, then analogously C++'s std::variant and std::get_if<>() and std::visit() (deeply flawed sad excuses) ought to be "good enough" to take the place of `match` and `if let`, and so is making `var` default and `const var` take extra typing and cluttering the code.


> Oh no. Anyway.

Languages encourage the behavior they make easy, and discourage the behavior they make difficult. Rust discourages making lambda captures explicit, because you need to pollute the outer scope or define a nested scope, and even with a nested scope, you can still access variables you didn't pseudo-capture in the nested scope (meaning you can't enforce explicit captures). Additionally I do not appreciate your flippant trolling and dismissal of my arguments.

> If it would, then you already do not need it, because you can use the precise capture pattern (a block expression around a `move` closure) in order to create your clone and the clone macro is just a convenience.

gtk-rs requires shared mutability. Rust discourages shared mutability. I find that pre-cloning shared pointers for every GUI method I want to bind sufficiently painful (as opposed to lacking an unneeded convenience) I would avoid using gtk-rs if not for the clone! macro (and even with it, subclassing was confusing enough and GTK4 so buggy on X11 that I quit anyway).

> That's literally just a repetition of your previous sentence.

No, my previous sentence (was intended to) describe capturing portions of structs (usually by value or trivial copy, always without mutating the original data), whereas this one describes invoking copy constructors of refcounted pointers within the capture block (mutating data visible by the original pointer).




Consider applying for YC's Fall 2026 batch! Applications are open till July 27.

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

Search: