Skip to content

RefTuple #1128

@Avid29

Description

@Avid29

Overview

Adding RefTuple<...> types would allow ref structs to be used in tuples.

API breakdown

public ref struct RefTuple<T1,T2>
    where T1 : allows ref struct
    where T2 : allows ref struct
{
    public RefTuple(T1 t1, T2 t2);

    public T1 Item1 { get; set; }

    public T2 Item2 { get; set; }

    public readonly void Deconstruct(out T1 item1, out T2 item2);
}

Usage example

This is an example using pattern matching to select a reference to a field, and a paired value. The ref is wrapped in a Ref<T>, which as a ref struct cannot be stored in a normal tuple or ValueTuple.

// Get reference to selected register argument
RefTuple<Ref<GPRegister>, RegisterSet> pair = target switch
{
    // General Purpose Registers
    Argument.RS => new(new(ref _rs), RegisterSet.GeneralPurpose),
    Argument.RT => new(new(ref _rt), RegisterSet.GeneralPurpose),
    Argument.RD => new(new(ref _rd), RegisterSet.GeneralPurpose),
    // Float Registers
    Argument.FS => new(new(ref _rs), RegisterSet.FloatingPoints),
    Argument.FT => new(new(ref _rt), RegisterSet.FloatingPoints),
    Argument.FD => new(new(ref _rd), RegisterSet.FloatingPoints),
    // RT Register for coprocessors
    Argument.RT_Numbered => new(new(ref _rt), RegisterSet.Numbered),
    // Invalid target type
    _ => throw new ArgumentOutOfRangeException($"Argument of type '{target}' attempted to parse as a register.")
};


(Ref<GPRegister> regRef, RegisterSet set) = pair;
ref GPRegister reg = ref regRef.Value;

Breaking change?

No

Alternatives

For the code above, here's what it looks like using a switch statement instead of a switch expression.

// Get reference to selected register argument
ref GPRegister reg = ref _rs;
RegisterSet set = RegisterSet.GeneralPurpose;
switch (target)
{
    // General Purpose Registers
    case Argument.RS:
        reg = ref _rs;
        break;
    case Argument.RT:
        reg = ref _rt;
        break;
    case Argument.RD:
        reg = ref _rd;
        break;
    // Float Registers
    case Argument.FS:
        reg = ref _rs;
        set = RegisterSet.FloatingPoints;
        break;
    case Argument.FT:
        reg = ref _rt;
        set = RegisterSet.FloatingPoints;
        break;
    case Argument.FD:
        reg = ref _rd;
        set = RegisterSet.FloatingPoints;
        break;
    // RT Register for coprocessors
    case Argument.RT_Numbered:
        reg = ref _rt;
        set = RegisterSet.Numbered;
        break;
    // Invalid target type
    default:
        return ThrowHelper.ThrowArgumentOutOfRangeException<bool>($"Argument of type '{target}' attempted to parse as a register.");
}

Perfectly legitimate code, but the pattern matching is much more convenient to read.

Additional context

This requires .NET 9 and C# version 13

Help us help you

Yes, I'd like to be assigned to work on this item

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature request 📬A request for new changes to improve functionality

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions