Skip to content

Commit c826aad

Browse files
author
José Valim
committed
Merge pull request #1655 from ericmj/fix-enum-slice-oob
Enum.slice returns nil when start is out of bounds
2 parents 25ffb4b + dbbfa35 commit c826aad

File tree

2 files changed

+35
-10
lines changed

2 files changed

+35
-10
lines changed

lib/elixir/lib/enum.ex

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,29 +1310,37 @@ defmodule Enum do
13101310
"""
13111311
@spec slice(t, integer, integer) :: list
13121312

1313-
def slice(_coll, _start, 0), do: []
1313+
def slice(coll, start, count) when start < 0 do
1314+
{ list, new_start } = iterate_and_count_oob(coll, start)
1315+
if new_start >= 0, do: slice(list, new_start, count)
1316+
end
13141317

1315-
def slice(coll, start, count) when is_list(coll) and start >= 0 and count >= 0 do
1318+
def slice(coll, start, count) when is_list(coll) and start >= 0 and count > 0 do
13161319
do_slice(coll, start, count)
13171320
end
13181321

1319-
def slice(coll, start, count) when start >= 0 and count >= 0 do
1320-
{ _, _, list } = Enumerable.reduce(coll, { start, count, [] }, fn
1322+
def slice(coll, start, count) when start >= 0 and count > 0 do
1323+
{ start, _, list } = Enumerable.reduce(coll, { start, count, [] }, fn
13211324
_entry, { start, count, _list } when start > 0 ->
13221325
{ start-1, count, [] }
13231326
entry, { start, count, list } when count > 1 ->
13241327
{ start, count-1, [entry|list] }
13251328
entry, { _start, _count, list } ->
13261329
throw { :enum_slice, [entry|list] }
13271330
end)
1328-
:lists.reverse(list)
1331+
1332+
if start <= 0, do: :lists.reverse(list)
13291333
catch
13301334
{ :enum_slice, list } -> :lists.reverse(list)
13311335
end
13321336

1333-
def slice(coll, start, count) when start < 0 do
1334-
{ list, start } = iterate_and_count(coll, start)
1335-
slice(list, start, count)
1337+
def slice(coll, start, 0) do
1338+
Enumerable.reduce(coll, start, fn _, start ->
1339+
if start > 0, do: start-1, else: throw :enum_slice
1340+
end)
1341+
nil
1342+
catch
1343+
:enum_slice -> []
13361344
end
13371345

13381346
## Helpers
@@ -1357,6 +1365,11 @@ defmodule Enum do
13571365
{ acc, buffer, i }
13581366
end
13591367

1368+
defp iterate_and_count_oob(collection, count) do
1369+
{ list, total_items } = do_iterate_and_count(collection)
1370+
{ list, total_items - abs(count) }
1371+
end
1372+
13601373
defp iterate_and_count(collection, count) do
13611374
{ list, total_items } = do_iterate_and_count(collection)
13621375
{ list, Kernel.max(0, total_items - abs(count)) }
@@ -1680,9 +1693,13 @@ defmodule Enum do
16801693

16811694
## slice
16821695

1683-
defp do_slice([], _start, _count), do: []
1696+
defp do_slice([], start, _count) do
1697+
if start == 0, do: []
1698+
end
16841699

1685-
defp do_slice(_list, _start, 0), do: []
1700+
defp do_slice(list, start, 0) do
1701+
if start < length(list), do: []
1702+
end
16861703

16871704
defp do_slice([h|t], 0, count) do
16881705
[h|do_slice(t, 0, count-1)]

lib/elixir/test/elixir/enum_test.exs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,10 @@ defmodule EnumTest.List do
320320
assert Enum.slice([1,2,3,4,5], 1, 0) == []
321321
assert Enum.slice([1,2,3,4,5], 2, 5) == [3, 4, 5]
322322
assert Enum.slice([1,2,3,4,5], 5, 5) == []
323+
assert Enum.slice([1,2,3,4,5], 6, 5) == nil
324+
assert Enum.slice([1,2,3,4,5], 6, 0) == nil
325+
assert Enum.slice([1,2,3,4,5], -6, 0) == nil
326+
assert Enum.slice([1,2,3,4,5], -6, 5) == nil
323327
assert Enum.slice([1,2,3,4,5], -2, 5) == [4, 5]
324328
assert Enum.slice([1,2,3,4,5], -3, 1) == [3]
325329
end
@@ -676,6 +680,10 @@ defmodule EnumTest.Range do
676680
assert Enum.slice(1..5, 1, 0) == []
677681
assert Enum.slice(1..5, 2, 5) == [3, 4, 5]
678682
assert Enum.slice(1..5, 5, 5) == []
683+
assert Enum.slice(1..5, 6, 5) == nil
684+
assert Enum.slice(1..5, 6, 0) == nil
685+
assert Enum.slice(1..5, -6, 0) == nil
686+
assert Enum.slice(1..5, -6, 5) == nil
679687
assert Enum.slice(1..5, -2, 5) == [4, 5]
680688
assert Enum.slice(1..5, -3, 1) == [3]
681689
end

0 commit comments

Comments
 (0)