Jelajahi Sumber

ranged-based `for` for user-defined types (#1885)

The goal of this proposal is to provide a way for user-defined types to
support range-based iteration with `for`.
The current proposed solution exposes 3 interfaces that can be
implemented by user types to enable support for
ranged-for loops.

Co-authored-by: josh11b <josh11b@users.noreply.github.com>

---------

Co-authored-by: josh11b <josh11b@users.noreply.github.com>
Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
Co-authored-by: Geoff Romer <gromer@google.com>
Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
Adrien Leravat 2 tahun lalu
induk
melakukan
1d4e9ee1ea
1 mengubah file dengan 870 tambahan dan 0 penghapusan
  1. 870 0
      proposals/p1885.md

+ 870 - 0
proposals/p1885.md

@@ -0,0 +1,870 @@
+# `for` statement and user types
+
+<!--
+Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+Exceptions. See /LICENSE for license information.
+SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+-->
+
+[Pull request](https://github.com/carbon-language/carbon-lang/pull/1885)
+
+<!-- toc -->
+
+## Table of contents
+
+-   [Abstract](#abstract)
+-   [Problem](#problem)
+-   [Background](#background)
+    -   [Other languages](#other-languages)
+        -   [C++](#c)
+        -   [Python](#python)
+        -   [Rust](#rust)
+        -   [Typescript](#typescript)
+        -   [Go](#go)
+-   [Proposal](#proposal)
+-   [Details](#details)
+    -   [`for` with immutable values](#for-with-immutable-values)
+        -   [`Iterate` interface](#iterate-interface)
+        -   [`let` by default](#let-by-default)
+        -   [Lifetime of rvalues on right-hand side of `:`](#lifetime-of-rvalues-on-right-hand-side-of-)
+    -   [Use cases](#use-cases)
+        -   [Mutable and immutable elements](#mutable-and-immutable-elements)
+        -   [Random access containers](#random-access-containers)
+        -   [Maps, hash maps, and other containers](#maps-hash-maps-and-other-containers)
+        -   [R-value containers](#r-value-containers)
+        -   [Synthetic data](#synthetic-data)
+        -   [Large containers](#large-containers)
+        -   [Filtering, transforming, and reversing](#filtering-transforming-and-reversing)
+        -   [Interoperability with C++ types and containers](#interoperability-with-c-types-and-containers)
+-   [Future work](#future-work)
+    -   [`for` with mutable values](#for-with-mutable-values)
+    -   [Speculative mixin usage](#speculative-mixin-usage)
+        -   [Mutable values](#mutable-values)
+        -   [Alternative views](#alternative-views)
+    -   [Large elements and Optional](#large-elements-and-optional)
+    -   [C++ interoperability](#c-interoperability)
+        -   [Iterating over C++ types in Carbon](#iterating-over-c-types-in-carbon)
+        -   [Iterating over Carbon types in C++](#iterating-over-carbon-types-in-c)
+    -   [Inversion of control](#inversion-of-control)
+-   [Rationale](#rationale)
+-   [Alternatives considered](#alternatives-considered)
+    -   [Atomic methods for `Iterate`](#atomic-methods-for-iterate)
+    -   [Using an iterator instead of a cursor](#using-an-iterator-instead-of-a-cursor)
+    -   [Support getter for both `T` and `T*` with `Iterate`](#support-getter-for-both-t-and-t-with-iterate)
+
+<!-- tocstop -->
+
+## Abstract
+
+This proposes an interface that can be implemented by user types to support
+range-based iteration with `for`.
+
+## Problem
+
+The current `for` proposal does not define a way for types to support
+range-based `for` loops.
+
+Examples of use cases for `for` loops include iterating over:
+
+-   Mutable and immutable elements
+-   Random access containers
+-   Maps, hash maps, and complex containers
+-   R-value containers
+-   Synthetic data
+-   Large containers, and large elements
+-   Filtering, transforming, and reversing
+-   Interoperability with C++ types and containers
+
+The goals for the solution includes:
+
+-   Easy to support for user types, with minimal requirements
+-   Simple and clear usage
+-   Minimal performance overhead
+
+## Background
+
+The original proposal of `for` loops were discussed in
+[p0353](/proposals/p0353.md), and the basic design documented in
+[control_flow/loops#for](/docs/design/control_flow/loops.md#for).
+
+The resulting syntax was:
+
+> `for (` _var declaration_ `in` _expression_ `) {` _statements_ `}`
+
+We now need a way to allow supporting this syntax for user and library container
+types.
+
+### Other languages
+
+This section highlights some examples of how iterators and range-based for
+user-defined types is addressed in different languages.
+
+#### C++
+
+C++ requires either `.begin()`/`.end()` methods, or
+`begin(<container>)`/`end(<container>)` to be supported for
+[range-based `for`](https://en.cppreference.com/w/cpp/language/range-for).
+
+#### Python
+
+Python requires 2 dunder methods, `__iter__` on the container and `__next__` on
+the iterator, to be usable with a `for <element> in <container>:` construct.
+
+[Generator functions](https://peps.python.org/pep-0255/) act like iterators.
+They are executed until the next `yield` statement, whose argument is returned
+as the next value to the `for` loop. Iteration ends when the function returns.
+
+```python
+def generate_fibonacci():
+  yield 0;
+  n1, n2 = 0, 1;
+  while True:
+    n1, n2 = n2, n1+n2;
+    yield n1;
+
+for n in generate():
+  print(n);
+```
+
+#### Rust
+
+Rust supports iterating over a container with
+[for](https://doc.rust-lang.org/rust-by-example/flow_control/for.html) using
+
+-   ranges `for n in 1..100`
+-   iterators `for element in container.iter()` or `.iter_mut()`
+
+The `Iterator` type can be used with custom types using the
+[following syntax](https://doc.rust-lang.org/rust-by-example/trait/iter.html)
+(though specific):
+
+```rust
+struct Fibonacci {
+    curr: u32,
+    next: u32,
+}
+
+impl Iterator for Fibonacci {
+    type Item = u32;
+    fn next(&mut self) -> Option<Self::Item> {
+        // calculate and yield value
+        [...]
+    }
+}
+
+for i in fibonacci().take(4) {
+    ...
+}
+```
+
+#### Typescript
+
+Typescript uses the
+[`.iterator` property](https://www.typescriptlang.org/docs/handbook/iterators-and-generators.html#iterables),
+along with
+[two forms](https://www.typescriptlang.org/docs/handbook/iterators-and-generators.html#forof-statements)
+of range-based for loops: `for..in` and `for..of`.
+
+```ts
+let pets = new Set(['Cat', 'Dog', 'Hamster']);
+pets['species'] = 'mammals';
+for (let pet in pets) {
+    console.log(pet); // "species"
+}
+for (let pet of pets) {
+    console.log(pet); // "Cat", "Dog", "Hamster"
+}
+```
+
+Using the `.iterator` property, Typescript allow to lazily generate data easily
+using functions, classes, or objects:
+
+```ts
+const reverse = (arr) => ({
+    [Symbol.iterator]() {
+        let i = arr.length;
+        return {
+            next: () => ({
+                value: arr[--i],
+                done: i < 0,
+            }),
+        };
+    },
+});
+```
+
+#### Go
+
+Go does not officially support customizing `for` for user types.
+
+## Proposal
+
+Provide interfaces that can be implemented by user types to enable support for
+ranged-for loops.
+
+We propose:
+
+-   A new `Iterate` interface to support for loops
+-   Using a cursor-based approach
+-   Making the `for` loop value `let` by default, that is, non-reassignable
+
+Below is a high-level example of this concept:
+
+```carbon
+// Add support for `for`
+class MyRange {
+  impl as Iterate where .ElementType = i32 and .CursorType = i32 {
+    // Implement `Iterate`
+  }
+}
+// Usage
+let my_range: MyRange = {...};
+for (item: auto in my_range) {
+  // Do something with `item`
+}
+```
+
+## Details
+
+### `for` with immutable values
+
+#### `Iterate` interface
+
+This proposal exposes a new `Iterate`. This interface:
+
+-   Relies on a cursor-based approach: minimize implementation effort for
+    developers, could support R-values.
+-   Expects an `ElementType` and `CursorType`.
+-   Combines "advance", "get", and "bounds check" into a single `Next(cursor)`
+    method that returns an optional value.
+
+The `Iterate` interface is:
+
+```carbon
+interface Iterate {
+  let ElementType:! type;
+  let CursorType:! type;
+  fn NewCursor[self: Self]() -> CursorType;
+  fn Next[self: Self](cursor: CursorType*) -> Optional(ElementType);
+}
+```
+
+A naive Carbon implementation of the for loop could be:
+
+```carbon
+var cursor: range.(Iterate.CursorType) = range.(Iterate.NewCursor)();
+var iter: Optional(range.(Iterate.ElementType)) = range.(Iterate.Next)(&cursor);
+
+// A. Possible implementation
+while (iter.HasValue()) {
+  ExecuteForBlock(iter.Get());
+  iter = container.(Iterate.Next)(&cursor);
+}
+
+// B. Possible alternative depending on the API for Optional(T):
+while (true) {
+  match (iter) {
+    case .None => { break; }
+    case .Some(let value: range.(Iterate.ElementType)) => {
+      ExecuteForBlock(value);
+      iter = container.(Iterate.Next)(&cursor);
+    }
+  }
+}
+```
+
+The cursor tracks progression, and the `Next` method advances the cursor and
+returns an `Optional` value. An empty `Optional` indicates that we have reached
+the end.
+
+Below is a sample implementation of that interface for a user type.
+
+```carbon
+class MyIntContainer {
+  var data: ...
+  fn Size[self: Self]() -> i32 {
+      return 4;
+  }
+  impl as Iterate where .ElementType = i32 and .CursorType = i32 {
+    fn NewCursor[self: Self]() -> i32 { return 0; }
+    fn Next[self: Self](cursor: i32*) -> Optional(i32) {
+      // Advance and return value, or return empty
+      if (*cursor < self.Size()) {
+        let index: i32 = *cursor;
+        *cursor = index + 1;
+        return Optional(i32).Create(self.data[index]);
+      } else {
+        return Optional(i32).CreateEmpty();
+      }
+    }
+  }
+}
+```
+
+Which can be used as follow:
+
+```carbon
+fn Main() -> i32 {
+  var container: MyIntContainer = {.data = (1, 2, 3, 4)};
+  // Implicit `let` value declaration
+  for (value: i32 in container) {
+    Print("{0}", value);
+  }
+  return 0;
+}
+```
+
+#### `let` by default
+
+To keep usage simple and non-surprising, we propose defaulting variable
+declarations to `let` declarations. This is consistent with function parameters
+and scrutinees for
+[pattern matching](https://github.com/carbon-language/carbon-lang/pull/2188),
+which are immutable by default.
+
+```carbon
+// Implicitly `let item: T`
+for (item: T in my_range) {
+  // `item` cannot be reassigned
+}
+```
+
+This default can be overridden by adding the `var` keyword:
+
+```carbon
+for (var item: T in my_range) {
+  // `item` can be modified, but this will not modify `my_range`
+  // because it is a copy of the element with its own storage
+}
+```
+
+#### Lifetime of rvalues on right-hand side of `:`
+
+It will be important to ensure that temporary entities on the right-hand side of
+`:` are alive during the execution of the `for` loop to prevent invalid memory
+access (Note: this was only recently addressed for C++ by
+[P2644](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2644r0.pdf)).
+This applies to Carbon and C++ interoperability.
+
+```carbon
+// Example using a temporary range, with `GetElements()` returning an object that implements `Iterate`
+for (item: T in bucket.GetElements()) {
+  // We need to make sure the object is alive for the duration of the `for` loop
+}
+```
+
+### Use cases
+
+This section covers the use cases highlighted in the introduction, and usage
+with the proposed design.
+
+#### Mutable and immutable elements
+
+This proposal covers strictly immutable elements, and proposes defaulting
+element declaration to `let`, to minimize surprises and clarify the intent.
+Mutable elements are mentioned in the [Future work](#future-work) section.
+
+#### Random access containers
+
+For random access containers, the cursor would represent a numerical index,
+incremented within `Next()`.
+
+#### Maps, hash maps, and other containers
+
+Because the cursor type is defined by the developer, and opaque to the user, it
+could be a hashmap index, a string, or pointer to a node, without changing the
+usage for users.
+
+The `ElementType` can be a tuple, such as a `(key, value)` for maps, or a single
+value. See [Future work][#future-work] for other examples.
+
+#### R-value containers
+
+R-values are likely to be limited in terms of addressing and mutation. The
+impact is undefined until the
+[corresponding design](https://github.com/carbon-language/carbon-lang/pull/2006)
+is finalized.
+
+The cursor approach used for `Iterate` has the slight advantage of not requiring
+pointers (and addressing) for some container types. This may be beneficial as a
+first iteration, until the dependent design is updated, at which point we will
+have to define how to handle this special case.
+
+#### Synthetic data
+
+`Next()` can be trivially implemented to return a single computed value
+on-demand based on the cursor, without the need for storage. If the sequence is
+infinite, `Next()` will never return an empty `Optional`, and the `for` loop
+will continue until interrupted by the user.
+
+```carbon
+class SquaredSequence {
+  impl as Iterate where .ElementType = i32 and .CursorType = i32 {
+    fn NewCursor[self: Self]() -> i32 { return -1; }
+    fn Next[self: Self](cursor: i32*) -> Optional(i32) {
+      // Advance and return computed value
+      *cursor = *cursor + 1;
+      return Optional(i32).Create((*cursor) * (*cursor));
+    }
+  }
+}
+```
+
+#### Large containers
+
+The proposal does not present any limitation regarding large collections. The
+cursor can be adapted to the collection size, and no copy of the container
+should occur both for immutable collections, and future mutable elements.
+
+#### Filtering, transforming, and reversing
+
+Provided the traversal order is the same, generic filters and transformers are
+supported by implementing a proxy `Iterate` interface, internally or externally.
+
+This example illustrates a simple proxy interface.
+
+```carbon
+class BasicFilter(T:! Iterate) {
+  var seq: T*;
+  var value: T.ElementType;
+  impl as Iterate where .ElementType = T.ElementType and .CursorType = T.CursorType {
+    fn NewCursor[self: Self]() -> T.ElementType { return self.seq->NewCursor(); }
+    fn Next[self: Self](cursor: T.CursorType*) -> Optional(T.ElementType) {
+      var res: auto = self.seq->Next(cursor);
+      while (res.has_value() && res.get() == value) {
+        res = self.seq->Next(cursor);
+      }
+      return res;
+    }
+  }
+}
+
+fn Main() -> i32 {
+  let container: MyIntContainer = MyIntContainer.Create(0,1,0,2,3,0);
+  let no_zeros: BasicFilter(MyIntContainer) =
+      { .seq = &container, .value = 0 };
+  // Prints "123"
+  for (v: i32 in no_zeros) {
+    Print("{0}", v);
+  }
+  return 0;
+}
+```
+
+On the other hand, changing how the collection is traversed (for example
+reverse), requires internal knowledge about the data layout, and a custom
+implementation. One approach for containers that support reverse iteration would
+be to implement a different interface with a `Prev()` method. A proxy could be
+exposed to make it usable with `for` directly.
+
+Below is an example of what such an interface could look like:
+
+```carbon
+interface IterateBidirectional;
+class ReverseProxy(T:! IterateBidirectional);
+interface IterateBidirectional {
+  extends Iterate;
+  fn NewEndCursor[self: Self]() -> CursorType;
+  fn Prev[self: Self](cursor: CursorType*) -> Optional(ElementType);
+  final fn Reversed[self: Self]() -> ReverseProxy(Self);
+}
+class ReverseProxy(T:! IterateBidirectional) {
+  var container: T;
+  impl as Iterate where .ElementType = T.ElementType and .CursorType = T.CursorType {
+    fn NewCursor[self: Self]() -> CursorType {
+      return self.container.NewEndCursor();
+    }
+    fn Next[self: Self](cursor: CursorType*) -> Optional(ElementType) {
+      return self.container.Prev(cursor);
+    }
+  }
+}
+
+fn IterateBidirectional.Reversed[self: Self]() -> ReverseProxy(Self) {
+  return {.container = self};
+}
+
+/// Usage
+class MyContainer {
+  fn Create(...) { ... }
+  impl as Iterate ... { ... }
+  impl as IterateBidirectional ... { ... }
+}
+var container: MyContainer = MyContainer.Create(...);
+for (v: container.(Iterate.ElementType) in container.Reversed()) {...}
+```
+
+#### Interoperability with C++ types and containers
+
+The cursor approach deviates from the C++ iterator approach, requiring some
+adaptation.
+
+Given the current state of the design, C++ interoperability is yet to be
+defined, and suggestions have been highlighted in the
+[Future work](#future-work) section.
+
+## Future work
+
+### `for` with mutable values
+
+Supporting mutable values could be achieved using a second interface that
+provides a proxy or view for the data, exposing an `Iterate` interface with
+`ElementType*`
+
+```carbon
+// Object implementing `MutableIterate` returned by a function
+for (p: T* in my_range.AddrRange()) {}
+```
+
+The example `MutableIterate` interface below acts as a proxy for the collection,
+and implements our `Iterate` interface.
+
+```carbon
+interface MutableIterate {
+  impl as Iterate;
+  alias ElementType = Iterate.ElementType;
+  let ProxyType:! Iterate where .ElementType = ElementType*
+      and .CursorType is ImplicitAs((Self as Iterate).CursorType);
+  fn AddrRange[addr self: Self*]() -> ProxyType;
+}
+
+class MyIntContainer {
+  //...
+  impl as Iterate ...
+  external impl as MutateIterate where .ProxyType = Self {
+    ...
+  }
+}
+```
+
+### Speculative mixin usage
+
+Mixins are currently in early design stages. This section highlights possible
+uses speculating on the final design. Some may include:
+
+-   Improved semantics for views compared to getter methods: no direct side
+    effect from using the view
+-   Facilitate code reuse, compared to reimplementing an interface
+-   Direct access to `self`, limiting needs for pointers and address resolution
+
+#### Mutable values
+
+Named mixins, used as programmable members, could expose a view with mutable
+values:
+
+```carbon
+// Using a named mixin to expose mutable values
+for (p: my_range.(Iterate.ElementType)* in my_range.mutable) {}
+```
+
+Used in this way, this clarifies that this a view of the data, like an
+attribute, with no direct side effects, when compared to a method.
+
+#### Alternative views
+
+Similarly, mixins could allow supporting other views into the container.
+
+For example, maps could expose a view with "key" and "value" only, in addition
+to the default (key, value) tuple.
+
+```carbon
+// Using a named mixin to expose mutable values
+for (k: my_dict.(Iterate.CursorType) in my_dict.keys) {}
+```
+
+And "reversed" view could also be used for reverse iteration:
+
+```carbon
+// Reversing
+for (v: my_dict.(Iterate.ElementType) in my_dict.reversed) {}
+```
+
+### Large elements and Optional
+
+Large elements would have a negative impact if the `Optional` cannot leverage
+unused bit patterns within the type, nor present a view instead of a copy.
+
+If this proves difficult, we can let implementers of the container choose
+whether to expose values or pointers to values. When electing to "pointers to
+values", an adapter can be provided to support transforming the range of
+pointers into a range a values. This choice allows supporting either iteration
+over container r-values (`ElementType = T`), or efficient iteration
+(`ElementType = T*`), but not both. While acceptable as an intermediate
+solution, a better alternative will be needed in the future.
+
+```carbon
+adapter IterateByValue(T:! Iterate where .CursorType impls Deref) for T {
+  impl as Iterate
+      where .CursorType = T.CursorType
+        and .ElementType = T.CursorType.(Deref.Result) {
+        ...
+  }
+}
+
+// Used like:
+var frames: NetworkFramesContainerByPtr = ...
+for (i: NetworkFrame in frames as IterateByValue(NetworkFramesContainerByPtr))
+{
+  ...
+}
+```
+
+Alternatively, we can allow the binding in the `for` loop to be an `addr`
+pattern, implemented by calling into a separate interface on the container. This
+puts the choice of using values or pointers to values in the hands of the `for`
+loop author. While this deviates from the ergonomics of `let` (where the
+compiler can choose to copy or alias the value depending on what is more
+efficient), this option would provide parity with C++ `for` loops (where the
+author chooses between value and reference).
+
+### C++ interoperability
+
+#### Iterating over C++ types in Carbon
+
+For reference, range-based `for` loops in C++ requires:
+
+-   `begin()` and `end()` methods or free functions, and
+-   the type returned supports pre-increment `++`, indirection `*`, and
+    inequality `!=` operations
+
+See [range-based for statement](https://eel.is/c++draft/stmt.iter#stmt.ranged)
+for more details.
+
+A limitation of the approach proposed in this document is the need for
+adaptation to interoperate with the C++ iterator model. The adapter would be a
+Carbon type that satisfies the Cursor interface, implemented in terms of a C++
+iterator.
+
+-   `NewCursor` providing the "start iterator" returned by `begin()`
+-   `Next` doing the "increment, bounds check, and returning value"
+
+Two different approaches are identified:
+
+-   One is to have a templated `impl` of `Iterate`, for anything with the right
+    method signatures (`begin`, `end`, and so on)
+-   Another is a templated adapter that implements `Iterate` when requested
+
+```carbon
+// Template impl approach
+template constraint CppIterator { ... }
+template constraint CppContainer {
+  // Speculative
+  for_unique IteratorType: CppIterator;
+  fn begin() -> IteratorType;
+  fn end() -> IteratorType;
+}
+external impl forall [template T:! CppContainer] T as Iterate
+    where .CursorType = T.(CppContainer.IteratorType)
+      and .ElementType = .CursorType.ElementType {
+  fn NewCursor[self: Self]() -> CursorType {
+    return self.(CppContainer.begin)();
+  }
+  fn Next[self: Self](cursor: CursorType*) -> Optional(ElementType) {
+    if (*cursor == self.(CppContainer.end)()) {
+      return Optional(ElementType).MakeEmpty();
+    }
+    let value: ElementType = **cursor;
+    ++*cursor;
+    return Optional(ElementType).MakeValue(value);
+  }
+}
+// Used like:
+var v: Cpp.std.vector(i32) = ...
+for (i: i32 in v) { ... }
+```
+
+```carbon
+// Adapter approach
+adapter CppIterate(template T:! type) for T {
+  impl as Iterate
+      // Speculative syntax. Alternatively, a cursor type parameter
+      // would avoid the need for deduction.
+      where .CursorType = typeof(T.begin())
+        and .ElementType = .CursorType.(Deref.Result) {
+    fn NewCursor[self: Self]() -> CursorType {
+      return self.begin();
+    }
+    fn Next[self: Self](cursor: CursorType*) -> Optional(ElementType) {
+      if (*cursor == self.end()) {
+        return Optional(ElementType).MakeEmpty();
+      }
+      let value: ElementType = **cursor;
+      ++*cursor;
+      return Optional(ElementType).MakeValue(value);
+    }
+  }
+}
+// Used like:
+var v: Cpp.std.vector(i32) = ...
+for (i: i32 in v as CppIterate(Cpp.std.vector(i32))) { ... }
+```
+
+These two options could allow interoperability with a compatible C++ type,
+either implicitly (template approach) or explicitly (adapter approach).
+
+#### Iterating over Carbon types in C++
+
+There need to be `begin()` and `end()` methods or free functions accessible to
+C++ for any Carbon type that implements the `Iterate` interface, in addition to
+overloads of the `*`, `++`, and `!=` operators for the types returned.
+
+The `Iterate` interface does not expose a way to have a cursor nor an iterator
+to the past-last element. An option would be to have `end()` return a predefined
+invalid value, to satisfy iterator comparison when `Next()` returns an empty
+`Optional`.
+
+Below is sample implementation to iterate on a Carbon `Iterate`. Note that we
+need to copy and cache the last value, to allow for `*it` to be called. This
+puts a requirement on `ElementType` to be copyable for this interoperability to
+be usable.
+
+```cpp
+namespace Carbon {
+// A C++ input iterator for any type that implements the Carbon `Iterate` interface.
+template <typename Iterate>
+class IterateIterator {
+ public:
+  // Returns an "Invalid" iterator representing `end()`
+  static auto Invalid(const Iterate& container) -> IterateIterator<Iterate> {
+    return IterateIterator<Iterate>(container, std::nullopt);
+  }
+
+  IterateIterator(const Iterate& container,
+                  std::optional<typename Iterate::CursorType> cursor)
+      : container_(&container), cursor_(cursor) {
+    if (cursor_) {
+      next();
+    }
+  }
+  IterateIterator(const IterateIterator& other)
+      : container_(other.container_), cursor_(other.cursor_),
+      value_(other) {}
+
+  auto operator*() const -> const typename Iterate::ElementType& {
+    assert(cursor_);
+    return value_;
+  }
+  auto operator++() -> IterateIterator<Iterate>& {
+    next();
+    return *this;
+  }
+  auto operator==(const IterateIterator<Iterate>& rhs) -> bool {
+    return container_ == rhs.container_ && cursor_ == rhs.cursor_;
+  }
+  auto operator!=(const IterateIterator<Iterate>& rhs) -> bool {
+    return !(*this == rhs);
+  }
+
+ private:
+  void next() {
+    assert(cursor_);
+    if (const auto v = container_->Next(*cursor_); v) {
+      value_ = *std::move(v);
+    } else {
+      cursor_ = std::nullopt;
+    }
+  }
+
+  const Iterate* container_;
+  std::optional<typename Iterate::CursorType> cursor_;
+  typename Iterate::ElementType value_;
+};
+
+// The C++ side of a custom Carbon type implementing
+// `Iterate` with CursorType `C`, and ElementType `T`
+// could have `begin()` and `end()` methods defined as:
+class MyIterateType {
+  // [...]
+  auto begin() -> IterateIterator<Iterate<C, T>> {
+    return IterateIterator<Iterate<C, T>>(*this, NewCursor());
+  }
+  auto end() -> IterateIterator<Iterate<C, T>> {
+    return IterateIterator<Iterate<C, T>>::Invalid(*this);
+  }
+};
+}  // namespace Carbon
+
+// Usage:
+Carbon::MyIterateType container;
+for (auto s : container) { /*...*/ }
+```
+
+### Inversion of control
+
+Using an `Optional` has downsides, discussed in the
+["alternatives considered"](#alternatives-considered) section. A possible
+approach would be to invert the control of the `for` loop, by having the
+container call in the `for` block for each value, passing the value as argument.
+This would work similarly to [Python generator functions](#python).
+
+This has the following advantages:
+
+-   Removes the need for an `Optional`, and the associated overheads, copies,
+    and unwrapping.
+-   No boundary checks needed at the `for` level
+-   Compatible with R-value containers
+
+Limitations:
+
+-   The context needed by the `for` body will needs to be available or provided
+-   No optimizer known to the team can reliably produce efficient code for this
+    construct.
+
+## Rationale
+
+This proposal offers a first step to support a needed way to iterate using a
+`for` loop. More specifically:
+
+-   Requires a single interface to be implemented with a combined `Next` method,
+    uses cursors instead of their more complex iterator counterpart, and defines
+    loop values as `let` by default to be less error prone
+    ([Code that is easy to read, understand, and write](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write)).
+-   Makes use of
+    [Library APIs only](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/principles/library_apis_only.md).
+
+## Alternatives considered
+
+### Atomic methods for `Iterate`
+
+An option was to define methods such as `Get`, `Advance`, `IsAtEnd` on
+`Iterate`, instead of a unique `Next` methods. While being more atomic, the
+concern is that these individual methods will all need to be called separately
+while iterating, while a single call to `Next` performs these three operations
+together, providing more opportunities for optimization.
+
+Using a single operation to return an optional is also consistent with Rust’s
+approach.
+
+The combined `Next` method forces us to return an `Optional`, which has
+downsides:
+
+-   potential performance overhead to wrapping and unwrapping,
+-   storage overhead for `Optional` itself, and
+-   may require additional copying of the value wrapped.
+
+Those may be mitigated by leveraging unused bit patterns, passing a view of the
+value, or [inverting control](#inversion-of-control).
+
+### Using an iterator instead of a cursor
+
+The iterator approach was considered but proved to have key limitations that a
+cursor approach does not have:
+
+-   It requires implementing 2 interfaces instead of 1, and is more complex due
+    to having the iteration logic separated from the container itself
+-   More difficult to harden or troubleshoot, compared to a cursor that allows
+    bounds checking in debug & hardened build modes
+-   Can pose problems with ranges that consumes their input (See Barry Revzin’s
+    "take(5)" presentation from C++Now 2023), when compared to a combined
+    `Next()` approach.
+-   Higher overhead
+-   Proves to be difficult to support for R-values
+
+On the other hand, the cursor approach deviates more from C++ than iterator, and
+may require some more effort for C++ interoperability.
+
+### Support getter for both `T` and `T*` with `Iterate`
+
+Because some collections will be read-only, we want to separate mutable and
+immutable interfaces. A single interface would force developers to implement or
+stub the mutable portion of the interface, even if it is not applicable.