Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

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

Discussion in 'Entity Component System' started by eterlan, Jun 24, 2019.

  1. eterlan

    eterlan

    Joined:
    Sep 29, 2018
    Posts:
    177
    Code (CSharp):
    1.         private NativeArray<State>  m_states;
    2.         private State[] m;
    3.         void Test()
    4.         {
    5.             ref var state = ref m_states[0]; // this doesn't works.
    6.             ref var state1 = ref m[0]; // this works.
    7.            
    8.             ModifyStateAction(ref m[0]); // this works.
    9.             ModifyStateAction(ref m_states[0]); // this doesn't works.
    10.         }
    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!
     
  2. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,753
    C# limitation from memory, I don't think you can do this with with list<t> either.
     
    eterlan likes this.
  3. eterlan

    eterlan

    Joined:
    Sep 29, 2018
    Posts:
    177
    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!
     
  4. JPrzemieniecki

    JPrzemieniecki

    Joined:
    Feb 7, 2013
    Posts:
    33
    Because the operator[] on NativeArray<T> (same for List<T> 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<T> as an example because NativeArray is a bit more complex, but the reasoning is the same.

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

    Code (CSharp):
    1. class List<T> {
    2.     T[] _internal_array;
    3.     public T this[int idx]{
    4.         return _internal_array[idx];
    5.     }
    6.     // this is equivalent to
    7.     // public T this[int idx]{
    8.     //    var temporary = _internal_array[idx];
    9.     //    return temporary;
    10.     // }
    11. }
    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:

    Code (CSharp):
    1. class MyList<T> {
    2.     T[] _internal_array;
    3.     public ref T this[int idx]{
    4.         return ref _internal_array[idx];
    5.     }
    6.     // equivalent:
    7.     // public ref T this[idx]{
    8.     //     var ref temporary = _internal_array[idx];
    9.     //     return temporary;
    10.     // }
    11. }
    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<T> op[] doesn't retun 'ref T'?
     
    Last edited: Jun 25, 2019
    eterlan likes this.
  5. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,223
    I believe it is because that ref result could be passed by ref to other contexts and generate race conditions.
     
  6. JPrzemieniecki

    JPrzemieniecki

    Joined:
    Feb 7, 2013
    Posts:
    33
    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).
     
  7. eterlan

    eterlan

    Joined:
    Sep 29, 2018
    Posts:
    177
    Thanks for your clear explanation!