Solidity: Range based loops

Created on 30 Oct 2020  路  11Comments  路  Source: ethereum/solidity

Range based loops were discussed several times, including at the Solidity Summit (#9054), in #4596, and in #9117.

Main motivation is improving code for analysis and reducing gas cost of checked array access.

This is a dedicated issue discussing the syntax and semantics:

uint[] array;

for (uint value: array) {}

// And with slicing
for (uint value: array[start:end]) {}

Of course for value types this doesn't support references, but references/pointers is probably something we should solve separately in general.

feature language design

Most helpful comment

If it was possible to make such references then I think it should have some special syntax.

Exactly, that's why I said "but references/pointers is probably something we should solve separately in general."

All 11 comments

Iterating over array of structs seems to be becoming increasingly more common, specially with the ABIEncoderV2 no longer being considered experimental. Supporting range-based loops for memory struct arrays would be fantastic. Could it also be made for calldata struct arrays?

I think it would be a generic feature, but limitations/complications is something we should discuss here. Also note in the above example slicing would only work on calldata at the moment anyhow.

Mentioning here so that it doesn't get lost in the discussion: a critical value add of this feature would be reduced gas costs, as the compiler would know it doesn't need to check out-of-bounds access when retrieving each element.

Something else to consider is what would happen would array mutability inside of the loop block, in particular, whether the array's length can change. Some languages (C++) leave it up for the developer to not invalidate the iterators, while others (Rust) disallow this sort of mutation altogether.

If we allow the array length to be changed, then we would need an sload/mload for each iteration to re-retrieve the length. I actually don't think we can safely get rid of that check anyway, so I would not pose any restrictions on the body.

I don't see why arrays of reference types should be any problem.

I don't see why arrays of reference types should be any problem.

By references I meant "pointers to value types", such that the range based loop could modify the values.

That's consistent with how local variables work so I think it's completely intuitive. If it was possible to make such references then I think it should have some special syntax.

This could be a use case for #1013 - explicitly making it an alias pointing at the current element would allow you to modify it.

If it was possible to make such references then I think it should have some special syntax.

Exactly, that's why I said "but references/pointers is probably something we should solve separately in general."

I would prefer if the length cannot change.

Something else to consider is what would happen would array mutability inside of the loop block, in particular, whether the array's length can change. Some languages (C++) leave it up for the developer to not invalidate the iterators, while others (Rust) disallow this sort of mutation altogether.

That's not completely true. Rust does disallow that by default, but it's because of a different reason. It doesn't simply add the explicit restriction "the container cannot be changed", it moves the container into immutable iterators, thus you can't change the container. You can also use mutable references and change the array, the language itself doesn't really care about you changing the array or not.
I think if we decide to disallow container changes it should be something generic like that, part of the type system, and not just an explicit "you can't change the array".

In the discussion the idea came up to also have indices support for the range based loops. Some possible scenarios where this is helpful:

  • Access to the elements of a second array with equal length. This of course would be even nicer solved with something like pythons zip function
  • Different logic based on the index, e.g. the first or last or every second element should be treated differently
  • Simple output or debugging of the index + the element for enumeration or such..

Another common scenario: implementing a find() function that looks for an element in an array and returns its index. It might be more common to implement something like this in Solidity than in other languages due to the lack of a rich standard library and generics. A variant of this would be also looking for a substring in a string.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

hiqua picture hiqua  路  4Comments

walter-weinmann picture walter-weinmann  路  4Comments

madvas picture madvas  路  3Comments

bshastry picture bshastry  路  3Comments

chriseth picture chriseth  路  3Comments