Skip to content

[API Proposal]: Safe API for covariant cast of ReadOnlySpan<T> #96952

@reflectronic

Description

@reflectronic

Background and motivation

It is always safe to convert a ReadOnlySpan<U> to a ReadOnlySpan<T> when U inherits from T (and neither are value types). However, performing such a cast is remarkably ugly and requires unsafe APIs:

public static ReadOnlySpan<object> FromStrings(ReadOnlySpan<string> stringSpan)
    => MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As<string, object>(ref MemoryMarshal.GetReference(stringSpan)), stringSpan.Length); 

A safe and easy-to-use API can be provided for performing such conversions.

API Proposal

namespace System;

public ref struct ReadOnlySpan<T>
{
    public static ReadOnlySpan<T> CastUp<TDerived>(ReadOnlySpan<TDerived> items) where TDerived : class, T;
}

There is prior art in ImmutableArray.CastUp which I have followed in this proposal.

API Usage

public static ReadOnlySpan<object> FromStrings(ReadOnlySpan<string> stringSpan)
    => ReadOnlySpan<object>.CastUp(stringSpan);

Alternative Designs

The method could be provided on MemoryExtensions as an extension method.

namespace System;

public static class MemoryExtensions
{
    // <T, TDerived> or <TDerived, T>?
    public static ReadOnlySpan<T> CastUp<T, TDerived>(this ReadOnlySpan<TDerived> items) where TDerived : class, T
}

However, type inference would not be possible for the type parameters:

public static ReadOnlySpan<object> FromStrings(ReadOnlySpan<string> stringSpan)
    => stringSpan.CastUp<object, string>(stringSpan);

It could be confusing to remember whether T or TDerived comes first in the generic type argument list.

This would be inconsistent with the existing ImmutableArray API. The difference is purely aesthetic, and presumably the ergonomic tradeoffs were considered during the review of that API, so sticking with the prior art seems best.

Risks

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions