Skip to content

Commit

Permalink
Make Iterator#cons_pair, #cycle, #flatten return classes
Browse files Browse the repository at this point in the history
  • Loading branch information
HertzDevil committed Nov 22, 2023
1 parent f8fafc3 commit f74d6aa
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 5 deletions.
32 changes: 32 additions & 0 deletions spec/std/iterator_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -1166,4 +1166,36 @@ describe Iterator do
iter.next.should be_a(Iterator::Stop)
end
end

describe "reference semantics (#12975)" do
it "#cycle" do
ary = [1, 2, 3]
iter1 = ary.each.cycle
iter2 = iter1
iter1.next
iter1.next
iter1.next
iter2.next.should eq(1)
iter1.next.should eq(2)
iter2.next.should eq(3)
end

it "#cons_pair" do
ary = [1, 2, 3]
iter1 = ary.each.cons_pair
iter2 = iter1
iter1.next
iter2.next.should eq({2, 3})
iter1.next.should be_a(Iterator::Stop)
end

it "#flatten" do
ary = [(1..2).each, ('a'..'b').each]
iter1 = ary.each.flatten
iter2 = iter1
iter1.next
iter2.next.should eq(2)
iter1.next.should eq('a')
end
end
end
4 changes: 2 additions & 2 deletions src/indexable.cr
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ module Indexable(T)
CartesianProductIteratorN(typeof(indexables), typeof(Enumerable.element_type Enumerable.element_type indexables)).new(indexables, reuse)
end

private class CartesianProductIteratorT(Is, Ts)
private struct CartesianProductIteratorT(Is, Ts)
include Iterator(Ts)

@indices : Array(Int32)
Expand Down Expand Up @@ -432,7 +432,7 @@ module Indexable(T)
end
end

private class CartesianProductIteratorN(Is, T)
private struct CartesianProductIteratorN(Is, T)
include Iterator(Array(T))

@indices : Array(Int32)
Expand Down
44 changes: 41 additions & 3 deletions src/iterator.cr
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,44 @@ require "./enumerable"
# Usually to get an iterator you invoke a method that would usually yield elements to a block,
# but without passing a block: `Array#each`, `Array#each_index`, `Hash#each`, `String#each_char`,
# `IO#each_line`, etc.
#
# ### Built-in iterator semantics
#
# All iterators in the standard library have reference semantics, regardless of
# whether they are defined as classes or structs. That means if an iterator is
# assigned to multiple variables, calling `#next` on any variable will affect
# the internal state of the other variables simultaneously:
#
# ```
# iter1 = (1..5).each.cons_pair
# iter2 = iter1
# iter1.next # => {1, 2}
# iter2.next # => {2, 3}
# iter1.is_a?(Reference) # => true
#
# iter1 = (1..5).each.step(2)
# iter2 = iter1
# iter1.next # => 1
# iter2.next # => 3
# iter1.is_a?(Reference) # => false
# ```
#
# For this reason, when chaining iterators, the standard library assumes that
# all input iterators also have reference semantics. One common cause of
# exhibiting value semantics is reassigning to an instance variable in `#next`'s
# body when an iterator is defined as a struct. Those iterators are not
# guaranteed to behave properly when chained:
#
# ```
# struct Zeros
# # same definition as above
# end
#
# iter1 = Zeros.new(1)
# iter2 = iter1
# iter1.next # => 0
# iter2.next # returns 0, but should be Iterator::Stop::INSTANCE
# ```
module Iterator(T)
include Enumerable(T)

Expand Down Expand Up @@ -477,7 +515,7 @@ module Iterator(T)
ConsTupleIterator(typeof(self), T).new(self)
end

private struct ConsTupleIterator(I, T)
private class ConsTupleIterator(I, T)
include Iterator({T, T})
include IteratorWrapper

Expand Down Expand Up @@ -519,7 +557,7 @@ module Iterator(T)
CycleIterator(typeof(self), T).new(self)
end

private struct CycleIterator(I, T)
private class CycleIterator(I, T)
include Iterator(T)
include IteratorWrapper

Expand Down Expand Up @@ -677,7 +715,7 @@ module Iterator(T)
FlattenIterator(typeof(FlattenIterator.iterator_type(self)), typeof(FlattenIterator.element_type(self))).new(self)
end

private struct FlattenIterator(I, T)
private class FlattenIterator(I, T)
include Iterator(T)

@iterator : I
Expand Down

0 comments on commit f74d6aa

Please sign in to comment.