Help with error "closure may outlive the current function, but it borrows..."

You're welcome :slight_smile:

Instead of cloning inside the thread, what is preventing the use of a move semantic of the value, not a borrow like what was tried at the very top of the stream? … in other words, use the transfer of ownership as a way to track the ability to safely mutate instead of the Rc/RefCell combo.

You can call .deref() explicitly, if you prefer to.

Yeah, and it's proposterously hard to parse (for the compiler). In fact it makes the grammar context-sensitive, so you have to run at least a partial type checking intertwined with the parser to stand a chance of correctly parsing (or in fact even lexing!) C code. Let's not even get into parsing C++ – some say that's outright undecidable, although I never saw a proof.

I don't find the spiral rule consistent, either. It's a rule of thumb that people came up with to explain the otherwise not well-designed typing syntax. But in the big picture, it's a mess:

  • why does the pointer designator bind to the variable name and not to the type?
  • why does the same happen with the array designator?
  • why does arr[] mean a real array in a variable declaration, but a pointer in a function declaration?
  • why does the type come first in the declaration instead of the name? This makes it hard to glance what kind of item you are reading (for the compiler and a human alike).
  • why should I be able to CV-qualify in both prefix and postfix position relative to a type, and have it mean the same thing, but only when there is no pointer involved?

These are all design errors that could absolutely have been avoided.

Rust's grammar, on the other hand, is context-free, with small lookahead. It is easy and fast to parse without resorting to hacks.

The type declaration syntax is not all there is to a language. Rust has a lot in common with C and C++. I've been programming in C++ for a long time, and I can confidently say that Rust is actually much more "C++ done right" than "OCaml/Haskell done popularly".

It is also the case that Rust designates correctness as its principal goal. That's hard to achieve in a whitespace language. It might not matter to the compiler, but explicit block delimiters help delineate sections of code unambiguously (even in the presence of eg. copy-pasting across media that mess up whitespace) and thus aid in writing code that was actually intended. Just think of the "goto fail" bug – it was a result of a design error in C's syntax along with faulty tooling, and it couldn't have happeed in Rust.

Have you tried to Google? The very first hit gives you this:

struct SomeType {};

template <...> struct TuringMachine {
  // Insert implementation of a Turing machine here, which we know
  // is possible from previous proofs.
};

template <typename T> struct S {
  static int name;
};

template<> struct S<SomeType> {
  typedef int name;
};

int x;
int main() {
  S<TuringMachine<...>::output>::name * x;
}

The parse tree for this program depends on whether TuringMachine::output is SomeType or not. If it is SomeType then ::name is an integer and the parse tree for the program is multiplying two integers and throwing away the result. If it is not SomeType, then ::name is a typedef for int and the parse tree is declaring a pointer-to-int named x. These two are completely different parse trees, and the difference between them cannot be delayed to further stages of the compiler.

Now, that's old article. You may say that you can limit what templates can do and then parsing becomes decidable (I think standard guarantees nesting of 128 levels or something like that). But in modern C++ you may just use constexpr function and implement turing machine that way.

In practice, of course, compilers place restrictions on how long constexpr function can work. But that's no longer question about whether C++ grammar is decidable or undecidable but about whether any particular compiler run with this or that options would accept your program or not. That's different question.

They could have been avoided if there was ever a design. But C evolved from B.

If you would look on early examples (available here) then you'll find out that there were no β€œspiral rule” and no confusion.

Pointers were declared as int ptr[] consistently everywhere, not just in parameters of functions.

Only this only syntax survived in parameters of functions while it was, eventually, eliminated from other places.

it's similar to how Rust have From and Info β€” vestigial remnants of prehistory which would probably survive for decades.

I'm not sure what you mean by that. From and Into are definitely a result of design, even if you don't agree with said design or how they work. I don't get which part of these two trivial traits offends you, but I can assure you that most of us like and use them every day.

It's not just me. When people who are developing Rust name these as an example of what can be done in Rust 2.0… you know that there's something wrong with them.

They are problematic for the same way blendConstants[4] is problematic as an argument of function in C (real example from real specificaion): needless confusion.

Yes, they are not as bad as arrays in C (where existing confusing syntax made important optimization more-or-less impossible), but they are still bad.

And if you'll bother to open the documentation you'll see that the only justification for Into is conversions to external types in old versions of Rust.

IOW: From/Into confusion are something which is no longer needed today and exist in the language as result of it's evolution.

Granted, array-passed-by-reference-even-if-it-doesn't-look-like-it syntax in C is both more dangerous and less useful than Into, yet backstory is the same.

I don't know any cases where it's still required to implement Into, but it's still a very useful blanket impl to have, and one that I think would still be added today were we to design a std 2.0 from the ground up. It's that useful to be able to write for<T: Into<String>> instead of for<T> where String: From<T>.

The ideal definition of Into may be a sealed trait (such that writing the From version isn't less permissive, and perhaps assisting type Inference), but that's still an undesigned feature.

Given the punctuation of the linked post, it makes me think they'd like From/Into to impact by-value coercion. The reasoning for this not being the case hasn't changed since 1.0, though. It'd be useful to have a way to opt into by-value coercions, sure, but a trait for explicit conversion is still useful to have. (C++ has explicit constructors for this reason.)

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.