D issues are now tracked on GitHub. This Bugzilla instance remains as a read-only archive.
Issue 3444 - foreach(i, elem; range) should work
Summary: foreach(i, elem; range) should work
Status: RESOLVED WONTFIX
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: Other Windows
: P2 enhancement
Assignee: No Owner
URL:
Keywords: patch
Depends on:
Blocks:
 
Reported: 2009-10-26 18:38 UTC by David Simcha
Modified: 2022-09-08 08:24 UTC (History)
5 users (show)

See Also:


Attachments
patch against dmd r526 (6.12 KB, patch)
2010-06-09 23:25 UTC, Shin Fujishiro
Details | Diff

Note You need to log in before you can comment on or make changes to this issue.
Description David Simcha 2009-10-26 18:38:59 UTC
Currently, one cannot do a foreach statement over a range that also gives the index.  This is inconsistent with arrays.  I'm not sure if it's the best fix, but at least a temporary fix is to mix this thing into all ranges:

template CountForeach(I) {
    int opApply(int delegate(ref I, ref typeof(this.front())) dg) {
        I index = 0;
        int result;
        foreach(elem; this) {
            result = dg(index, elem);
            if(result) {
                break;
            }
            ++index;
        }

        return result;
    }
}

Usage:

struct SomeRange {

    SomeType front() { return something; }

    void popFront() {  
        doStuff();
    }

    bool empty() {
        return amIEmpty();
    }
   
    mixin CountForeach!size_t;
}

void main() {
    SomeRange someRange;
    
    foreach(elem; someRange) {}  // Uses range interface directly.

    foreach(i, elem; someRange) {}  // Uses the mixin;
}
Comment 1 David Simcha 2009-12-09 20:03:17 UTC
Fixed.
Comment 2 David Simcha 2009-12-09 20:37:20 UTC
Argh, accidentally marked the wrong bug fixed.
Comment 3 Shin Fujishiro 2010-06-09 23:25:08 UTC
Created attachment 659 [details]
patch against dmd r526

Implemented foreach(i, e; r) and foreach_reverse(i, e; r).
For reverse iteration with index, r must have a length property.
Comment 4 RazvanN 2018-02-15 12:52:26 UTC
This has been implemented. Closing as fixed.
Comment 5 Simen Kjaeraas 2018-02-15 13:10:23 UTC
Really? Cause this fails to compile for me:

unittest {
    import std.range, std.stdio;
    foreach (i, e; 100.iota)
        writeln(i, ": ", e);
}

Error: cannot infer argument types, expected 1 argument, not 2

Adding types for i and e, or using ranges other than iota, don't seem to change the result. This is on DMD 2.078.2 on Windows.

There's a workaround in std.range.enumerate. If that's the implementation you refer to, please include that information. If there's an implementation of this in an upcoming DMD release, please include that information.
Comment 6 Dmitry Olshansky 2018-05-16 09:02:54 UTC
(In reply to Simen Kjaeraas from comment #5)
> Really? Cause this fails to compile for me:
> 
> unittest {
>     import std.range, std.stdio;
>     foreach (i, e; 100.iota)
>         writeln(i, ": ", e);
> }
> 
> Error: cannot infer argument types, expected 1 argument, not 2
> 
> Adding types for i and e, or using ranges other than iota, don't seem to
> change the result. This is on DMD 2.078.2 on Windows.
> 
> There's a workaround in std.range.enumerate. If that's the implementation
> you refer to, please include that information. If there's an implementation
> of this in an upcoming DMD release, please include that information.

This works today. And arguably there many other use cases that could work in the same way, so I do doubt the niche case of 0..n indexing will be worth 
the enhancement to the compiler especially as it uses the same exact syntax as tuple unpacking.


void main() {
    import std.range, std.stdio;
    foreach (i, e; 100.iota.enumerate)
        writeln(i, ": ", e);
}
Comment 7 RazvanN 2022-09-08 08:24:34 UTC
(In reply to Simen Kjaeraas from comment #5)
> Really? Cause this fails to compile for me:
> 
> unittest {
>     import std.range, std.stdio;
>     foreach (i, e; 100.iota)
>         writeln(i, ": ", e);
> }
> 
> Error: cannot infer argument types, expected 1 argument, not 2
> 
> Adding types for i and e, or using ranges other than iota, don't seem to
> change the result. This is on DMD 2.078.2 on Windows.
> 
> There's a workaround in std.range.enumerate. If that's the implementation
> you refer to, please include that information. If there's an implementation
> of this in an upcoming DMD release, please include that information.

I was referring to the enumarate workaround. Sorry for not being explicit.

As Dmitri has pointed out, the additional complexities added to support this aren't worth it since we have a decent workaround.