Last Thursday at our monthly ScotRUG meetup we did Ruby Koans. To my surprise it was very educational, probably because of Ruby being so
inconsistent and unpredictable comprehensive and feature rich. Ceri Shaw (who reads C code much better than I do) and I paired over some unexpected array slicing behaviour and found out how it’s wired under the hood.
We investigated the slice method that takes two arguments:
Now let’s take a look at edge cases. Namely at out of range slicing.
Wait, what? The last index of
ar is 2, so why
ar[4,1] return different results if they both are out of range?
The logic behind this, as James Bell put it, is that slices start between the elements. For
ar[3,1] slice starts right before the 3rd element (or after 2nd element) and takes 1 element. Because there are no elements after the index 2 it return an empty array. And
ar[4,1] starts right after the 3rd element, which is out of range and therefore returns
Let’s take a look on the implementation. Below is source code of the
If 2 arguments (starting index and the length) were passed it calls
Let’s break down what is going on there:
If starting index is larger than array length return
nil. This is the condition that executes for
ar[4,1] as the length of
ar = [1,2,3] is
4 > 3.
If either starting index or length are less than 0 return
nil. The caller function
rb_ary_aref takes care of negative starting indexes transforming them to indexes “from the end” with
beg += RARRAY_LEN(ary);
If the length of requested slice is larger than the length of the array or if the last element of the requested slice falls out of range take only the elements from the starting index to the end of the array;
Finally if the length of the requested slice is
0 return an empty array. And from the previous line it’s clear that for
ar = [1,2,3]
len would be equal to
So here it is, the logic and the implementation of Ruby array slicing.comments powered by Disqus