Mani
Mani

Reputation: 2563

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?

Upvotes: 6

Views: 149

Answers (1)

Skrat
Skrat

Reputation: 587

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);

Upvotes: 2

Related Questions