foreach ref very broken: fails to call front(val)

kenji hara k.hara.pg at gmail.com
Tue Jul 10 01:29:36 PDT 2012


Posted a pull request:
https://github.com/D-Programming-Language/phobos/pull/678

With the patch, following code works as expected.

import std.container;
import std.stdio;
import std.algorithm;
void main()
{
    Array!int arr;
    arr.length = 3;

    foreach(ref a; arr)   // requre 'ref'
        a = 2;

    writeln(arr[]);
    // prints [2,2,2]

    // Use writeln to evaluate all elements of map range.
    map!("a+=2")(arr[]).writeln();
    // prints [4,4,4]
}

2012/7/10 kenji hara <k.hara.pg at gmail.com>:
> There are two problems:
>
> 1. std.container.Array.(front, back, opIndex, ...) doesn't return its
> elements by ref.
> 2. std.algorithm.map doesn't consider original range's ref-ness.
>
> Bye.
>
> Kenji Hara
>
> 2012/7/2 monarch_dodra <monarch_dodra at gmail.com>:
>> I think this is a pretty serious bug: when one writes: "foreach(ref a,
>> range)", the underlying (ref'd) object will ONLY get modified if the range
>> object provides a "ref T front()" method.
>>
>> What is worrisome is that:
>> a) The code compiles anyways.
>> b) This even happens when range provides a "void front(T t)" method.
>>
>> Here is the bug in action, with the very standard Array class, as well as
>> the generic Map algorithm:
>> ----
>> import std.container;
>> import std.stdio;
>> import std.algorithm;
>>
>> void main()
>> {
>>   Array!int arr;
>>   arr.length = 3;
>>
>>   foreach(a; arr)
>>     a = 2;
>>
>>   map!("a+=2")(arr[]);
>>
>>   writeln(arr[]);
>> }
>> ----
>> Output:
>> ----
>> [0, 0, 0]
>> ----
>>
>> As you can see, not only is "foreach" broken, but so is any algorithm that
>> tries to mutate an Array.
>>
>> Note that "Array" itself can be fixed by my recommendation here:
>> http://forum.dlang.org/thread/bkozswmsgeibarowfwvq@forum.dlang.org
>>
>> However, in the case of containers that can't return a ref'ed front (eg
>> Array!bool), this is actually a double bug:
>>
>> 1) foreach: It makes compile time call to "T front()" regardless of context.
>> Because of this, even when we write "a = 2", a straight up assignment to a
>> temporary takes place, rather than compiling as "arr.front(2)".
>>
>> 2) front: Unlike opIndex, front is not an operator. This means that writing
>> code such as "arr.front += 5" or "++arr.front" is not supported by the
>> language (when front returns by value). This means the implementer of a
>> Range (which can't return a ref'd front) has absolutely no way of
>> intercepting operations that want to occur directly on front


More information about the Digitalmars-d mailing list