[Issue 18657] New: std.range and std.algorithm can't handle refRange
d-bugmail at puremagic.com
d-bugmail at puremagic.com
Sat Mar 24 13:49:22 UTC 2018
https://issues.dlang.org/show_bug.cgi?id=18657
Issue ID: 18657
Summary: std.range and std.algorithm can't handle refRange
Product: D
Version: D2
Hardware: All
OS: All
Status: NEW
Severity: normal
Priority: P1
Component: phobos
Assignee: nobody at puremagic.com
Reporter: ag0aep6g at gmail.com
Five examples:
----
void main()
{
import std.range: refRange;
import std.stdio;
{
import std.algorithm.iteration: group;
string s = "foo";
auto r = refRange(&s).group;
writeln(r.save);
/* Prints
"[Tuple!(dchar, uint)('f', 1), Tuple!(dchar, uint)('o', 2)]". Ok.
*/
writeln(r.save);
/* Should print the same as the line above. Actually prints
"[Tuple!(dchar, uint)('f', 1)]". */
}
{
import std.range: chain;
string s = "foo";
auto r = refRange(&s).chain("bar");
writeln(r.save); /* Should print "foobar". Actually prints "bar". */
}
{
import std.range: choose;
string s = "foo";
auto r = choose(true, refRange(&s), "bar");
writeln(r); /* Should print "foo". Actually prints nothing. */
}
{
import std.range: cycle, take;
string s = "foo";
auto r = refRange(&s).cycle.take(4);
writeln(r.save); /* Prints "foof". Ok. */
writeln(r.save); /* Should print "foof", too. Actually prints "oofo".
*/
}
{
import std.algorithm.iteration: splitter;
string s = "foobar";
auto r = refRange(&s).splitter!(c => c == 'b');
writeln(r.save); /* Prints "[foo, ar]". Ok. */
writeln(r.save);
/* Should print the same. Actually crashes with an AssertError. */
}
}
----
Most probably, there are more Phobos functions that can't handle refRange. I
haven't checked them all.
The root of the problem is RefRange's opAssign. Instead of just changing the
reference, it actually overwrites the referenced range. That leads to
surprising behavior:
----
void main()
{
import std.range;
import std.stdio;
string s = "foo";
auto r = refRange(&s);
auto r2 = r;
r2 = r2.save;
/* Surprising: Effectively just does `s = s;` (i.e., nothing). */
r2.popFront();
writeln(r); /* Surprising: Prints "oo". */
}
----
Note that `r2 = r; r2 = r2.save;` is what you typically do in a postblit
function.
If RefRange's custom opAssign is removed, all the examples just work.
Unfortunately, the surprising behavior is deliberate, and not just a bug. The
docs on RefRange.opAssign say [1]:
> This does not assign the pointer of rhs to this RefRange.
> Rather it assigns the range pointed to by rhs to the range pointed
> to by this RefRange. This is because any operation on a RefRange is
> the same is if it occurred to the original range.
The issue comes down to whether RefRange should be allowed to have its funky
opAssign, or if range-handling code should be allowed to assume that assignment
does the obvious thing.
[1] https://dlang.org/phobos/std_range.html#.RefRange.opAssign
--
More information about the Digitalmars-d-bugs
mailing list