On Monday, 27 May 2024 at 06:31:37 UTC, Jonathan M Davis wrote:
> I don't recall ever really thinking about it. I don't think that it's something that I've done very often, and when I have, it was probably for debugging. And in many cases, if you want readable input, it makes sense to use foreach to loop through the outer range and print out each inner range individually, in which case, you can call save on the inner ranges. That's usually what I'd do if I know that I'm printing out a range of ranges.
This is what writeln
does (loops over the individual elements). You can even format the nested ranges using writefln
and the %(...%)
format specifier.
> Given that writeln needs to work with basic input ranges, having it not consume the inner ranges would result in different behavior between basic input ranges and forward ranges, which wouldn't be great. So, arguably, having it consume them is the correct choice, but I'd have to spend a fair bit of time thinking through the implications to come to a properly informed conclusion.
I don't think you are grasping how surprising this is. If you are debugging something, and you want to see what something looks like at the moment, you print it. In this case, the act of printing modifies the thing you are debugging! And it doesn't even look like it did anything, because it printed fine. It's only on the second printing you see there is a problem. So you think "what happened between the first printing and the second printing?".
This is actually the use case I was looking at yesterday when I discovered (probably rediscovered) this issue. Would you expect printing a struct to modify the struct? Well, it does if it includes one of these range-of-ranges!
Note also, if you make the outer range by-ref, it will consume all the inner ranges but not the outer range, even if it uses by-ref elements. In other words, it has different behavior on the outer range, vs the inner range. This is because writeln
accepts its parameters by value, but the underlying formatValue
uses auto ref (to support non-copyable range elements).
And, by the way, nested arrays are not consumed, even if they are inside a range with lvalue elements. So that is another outlier. And also likely why nobody has complained about this -- most people use arrays for their ranges.
> Realistically though, I expect that it's an issue that was never really thought through, and the current behavior is accidental whether it's truly desirable behavior or not.
I tend to agree. I'm going to file an issue on it. I think any forward ranges should be passed via .save
to their respective formatters. This should fix the problem, and is what most people would expect.
When I posed this question, my thought was that the behavior was unintuitive, but given the length of time this has existed, I thought maybe someone has a good reason why the code is this way, and I'm just not seeing it.
Note for the range redesign -- this is going to make things tricky as we won't have a save
to use. We will have to explicitly copy the range before passing to the formatValue
function (as long as it's a forward range). This is kind of a drawback, I'll put that on the range redesign thread.
-Steve