Why struct as an element of nativeArray cannot assign variable with ref ?

        private NativeArray<State>  m_states;
        private State[] m;
        void Test()
        {
            ref var state = ref m_states[0]; // this doesn't works.
            ref var state1 = ref m[0]; // this works.
            
            ModifyStateAction(ref m[0]); // this works.
            ModifyStateAction(ref m_states[0]); // this doesn't works.
        }

Exception:
Indexer access returns temporary value.‘ref’ argument must be an assignable variable, field or an array element.

Any idea about that? Thanks a lot!

C# limitation from memory, I don’t think you can do this with with list either.

1 Like

Yes, you are right. I have a nested NativeArray in the struct State, that’s why I can’t simply assign a new struct. But I found out I can just modify the NativeArray, so no problem now, thanks for your help!

Because the operator[ ] on NativeArray (same for List etc) is a method that returns ‘T’, not ‘ref T’.
The built-in array T[ ] is somewhat special in that it behaves as if it returned a ‘ref T’ (this behaviour actually predates the introduction of ref returns into the language).

EDIT: To expand on this a bit.
I will use List as an example because NativeArray is a bit more complex, but the reasoning is the same.

List overloads the operator[ ] with something that looks like this:

class List<T> {
    T[] _internal_array;
    public T this[int idx]{
        return _internal_array[idx];
    }
    // this is equivalent to
    // public T this[int idx]{
    //    var temporary = _internal_array[idx];
    //    return temporary;
    // }
}

What happens when you call myList[5] is:
The fifth element of _internal_array is copied into a temporary storage place (‘onto the stack’)
That temporary is returned.

So now when you try to do var ref x = ref myList[5]; you are creating a reference to what myList[5] returns, which is that temporary storage.
This is never what you actually want and the compiler throws an error when you try.

C#7 introduced a concept of ‘ref return’, allowing you to write something like:

class MyList<T> {
    T[] _internal_array;
    public ref T this[int idx]{
        return ref _internal_array[idx];
    }
    // equivalent:
    // public ref T this[idx]{
    //     var ref temporary = _internal_array[idx];
    //     return temporary;
    // }
}

Here the method itself returns a ref T, so doing var ref x = ref myList[5]; doesn’t try to create a reference, it just stores the reference that operator[ ] already created for you.
/EDIT

What I would be interested in is: does anyone know the reason NativeArray op[ ] doesn’t retun ‘ref T’?

1 Like

I believe it is because that ref result could be passed by ref to other contexts and generate race conditions.

How would you get it into another context? (you can’t store a ref into persistent storage, both ref variables and ref structs have to live on the stack).

Thanks for your clear explanation!