One of Rust core features is immutability. But Rust currently doesn't have an immutable loop structure, just iterator methods. My proposal is the loop let structure which sorta allow for recursion that auto unwraps into an iteration. Actually, every tail call recursive algorithm could be implemented using loop let being tail-call-optimized for free.
Idea
The loop let could be used as a looping structure that is also an expression. It has an initial value, then at the end of each iteration the developer must return the value for the next iteration or the final value of the loop expression finishing it.
This syntax wouldn't add any new reserved keywords, which let's the implementation easier and not introduce any breaking change. Also it's similar to current if let and while let syntax, except it won't accept fallible patterns if used as an expression.
Also besides accepting break b and continue c statements to short-circuit an iteration, loop let could accept a value of type ControlFlow<B, C> at the end of the block where B is the output type (type of the loop let when used as an expression) and C is the input type (type of the initial value).
Example
The example below implements a simple fibonacci function that doesn't need recursion, but rather uses loop let with (u64, u64, u64) as its input and outputs u64.
Nit arguably, but I don't really agree with this.[1][2][3]
Put another way, it’s become clear to me over time that the problems with data races and memory safety arise when you have both aliasing and mutability. The functional approach to solving this problem is to remove mutability. Rust’s approach would be to remove aliasing. This gives us a story to tell and helps to set us apart.
E.g. you can make all bindings of any program mut without changing the semantics, so requiring mut on bindings is arguably nothing more than a lint. ↩︎
E.g. in a generic or foreign type context you can't rule out interior mutability, and no, the unstable Freeze trait doesn't help as that covers shallow interior mutability only (for example nothing on the other side of a Box or other pointer, or that round-trips through the OS or other I/O). ↩︎
Though unfortunately most learning resources, including official ones, present (or at least open with) a mutable-vs-immutable model verus the more accurate exclusive-vs-shared model. ↩︎
The real benefit of tail calls is when you have different functions calling each other in a tail-recursive way. You cannot do that with a loop like this.
First of all guys thanks for the feedback, as I said this is just a scratch of the idea in my mind, but trust me, it can go beyond the current ways to achieve similar behaviour
Your repeat function is just a partial implementation of loop let since repeat doesn't support fallible patterns, while loop let does support.
If fact, loop let would need some special treatment to support this crossed recursion
We still don't have become and i dont think we'll ever see it. Also become is a terrible choice of name, recur would be a better option.
Again thanks for the feedback it helps me strengthen the proposal
As in, the result of this loop let example is defining the bindings n, curr and next not just inside its block but also into the enclosing block (like a let), rather than ultimately evaluating into an expression without defining any bindings.
The behaviour or loop let is sorta a mix of loop and while let
The value passed to continue must be the same as in the input. The value passed to break is the value of the loop let expression, they don't need to be the same. Also, just like in if let and while let binds are local to the block, so curr isn't defined outside the loop block.