Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange behaviour of Array element assignment

Today I came across some strange behaviour of Array element assignment:

arr = ["a","b"]
arr2 = [1,2]
arr.unshift(arr2) #= [[1, 2], "a", "b"] 
arr.push(arr2) #=> ["a", "b", [1, 2]] 

This makes sense, however:

arr[0,0] = arr2 #=> [1, 2, "a", "b"] 

I know that in [0,0] the first zero is index and second is the number of elements to be effected in that array starting from index.

In my thoughts it should be the same as the unshift, but it's not.

Can any one explain the behavior?

like image 486
Mani Avatar asked Feb 11 '16 18:02

Mani


1 Answers

If we dive into the ruby source code, we'll find a function named rb_ary_splice called when array assignment happens with three arguments (i.e. index, length, and new value):

static VALUE
rb_ary_aset(int argc, VALUE *argv, VALUE ary)
{
    long offset, beg, len;

    if (argc == 3) {
        rb_ary_modify_check(ary);
        beg = NUM2LONG(argv[0]);
        len = NUM2LONG(argv[1]);
        rb_ary_splice(ary, beg, len, argv[2]);
        return argv[2];
    }
[...]

And if we follow along in rb_ary_splice we'll happen upon where the magic happens:

static void
rb_ary_splice(VALUE ary, long beg, long len, VALUE rpl)
{
    long rlen;
    long olen;

    if (len < 0) rb_raise(rb_eIndexError, "negative length (%ld)", len);
    olen = RARRAY_LEN(ary);

    [...]

        if (len != rlen) {
            RARRAY_PTR_USE(ary, ptr,
                   MEMMOVE(ptr + beg + rlen, ptr + beg + len,
                       VALUE, olen - (beg + len)));
            ARY_SET_LEN(ary, alen);
        }
        if (rlen > 0) {
            MEMMOVE(RARRAY_PTR(ary) + beg, RARRAY_CONST_PTR(rpl), VALUE, rlen);
        }
    }
    RB_GC_GUARD(rpl);
}

First it makes room in the array for the new elements and updates the length:

RARRAY_PTR_USE(ary, ptr,
    MEMMOVE(ptr + beg + rlen, ptr + beg + len,
        VALUE, olen - (beg + len)));
ARY_SET_LEN(ary, alen);

Then through the magic of C pointers, it inserts the new element(s):

MEMMOVE(RARRAY_PTR(ary) + beg, RARRAY_CONST_PTR(rpl), VALUE, rlen);
like image 198
Skrat Avatar answered Oct 23 '22 01:10

Skrat