[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