Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ImmutableArray<T>.Builder.AsSpan #43760

Closed
alrz opened this issue Oct 23, 2020 · 13 comments
Closed

Add ImmutableArray<T>.Builder.AsSpan #43760

alrz opened this issue Oct 23, 2020 · 13 comments
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Collections

Comments

@alrz
Copy link
Member

alrz commented Oct 23, 2020

Background and Motivation

Before calling ToImmutable and making a copy, you may want to access the inner array as a Span. Unlike MoveToImmutable which also doesn't incur a copy, this does not require Count == Capacity as AsSpan can only expose elements up to Count.

Proposed API

namespace System.Collections.Immutable
{
    public partial struct ImmutableArray<T>
    {
        public sealed class Builder
        {
+            public Span<T> AsSpan();
        }
     }
}

The implementation is straightforward:

public Span<T> AsSpan() => new Span<T>(_elements, 0, this.Count);

We may want to add a similar member to other builders as well.

@alrz alrz added the api-suggestion Early API idea and discussion, it is NOT ready for implementation label Oct 23, 2020
@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added area-System.Collections untriaged New issue has not been triaged by the area owner labels Oct 23, 2020
@ghost
Copy link

ghost commented Oct 23, 2020

Tagging subscribers to this area: @eiriktsarpalis, @jeffhandley
See info in area-owners.md if you want to be subscribed.

@alrz
Copy link
Member Author

alrz commented Oct 23, 2020

There is a similar (internal) API System.Collections.Generic.ValueListBuilder<T>.AsSpan() which returns a ReadOnlySpan<T>. I don't think that's helpful because we're still in the builder, so modifications should be allowed.

@eiriktsarpalis
Copy link
Member

Related to #21727. I feel the concerns raised there also apply here. And given that immutable builders are niche compared to List<T> I would expect that such an addition would not be accepted. cc @stephentoub

@eiriktsarpalis eiriktsarpalis removed the untriaged New issue has not been triaged by the area owner label Oct 23, 2020
@alrz
Copy link
Member Author

alrz commented Oct 23, 2020

given that immutable builders are niche compared to List I would expect that such an addition would not be accepted

To give some context here, I wanted to use this in Roslyn's ArrayBuilder type (a pooled wrapper around IA<T>.Builder) for an efficient post-processing before I get to call ToImmutable/Free.

@alrz
Copy link
Member Author

alrz commented Oct 23, 2020

I think the main concern is that any modification to the builder after AsSpan call would be unsafe.

Is there any "unsafe" way to get the span then? so that if someone is aware of the consequence at least they could save a copy

@Joe4evr
Copy link
Contributor

Joe4evr commented Oct 23, 2020

I recall there was some discussion to have methods like this declared in CollectionMarshal, so that it's something a dev has to go out of their way for to look and (in theory) be aware that they are doing somewhat unsafe things.

It could be considered further if the Caller-Must-Be-Unsafe analyzer (#31354) gets done and shipped, so that anyone who wants to call this method knows that the rules of an unsafe context apply.

@mayorovp
Copy link
Contributor

This api will be unsound:

ImmutableArray<int>.Builder builder = ...;
builder[0] = 5;

var span = builder.AsSpan();
ImmutableArray<int> array = builder.MoveToImmutable();

Console.WriteLine(array[0]); // 5
span[0] = 6;
Console.WriteLine(array[0]); // 6

@alrz
Copy link
Member Author

alrz commented Oct 23, 2020

I think if we empty the original array just like MoveToImmutable all concerns are addressed. It is a MoveToImmutable but without Count == Capacity requirement.

Might as well rename to MoveToSpan?

public ReadOnlySpan<T> MoveToSpan()
{
    T[] temp = _elements;
    _elements = ImmutableArray<T>.Empty.array!;
    _count = 0;
    return new ReadOnlySpan<T>(temp, 0, Count);
}

@mayorovp
Copy link
Contributor

But ImmutableArray<T>.Builder is a builder for ImmutableArray<T>, not for Span<T>...

@alrz
Copy link
Member Author

alrz commented Oct 24, 2020

It seems wasteful to make a builder just for span. They use the same underlying type: array. So to me it makes sense to have a helper like that.

This is more important now that MoveToImmutable requires the same Capacity as Count. That's a huge limitation when you want to avoid the copy.

@mayorovp
Copy link
Contributor

Yes, it is wasteful to make a ImmutableArray<T>.Builder just for span.

@SingleAccretion
Copy link
Contributor

Related: #37852 and #27061.

@alrz
Copy link
Member Author

alrz commented Nov 3, 2020

I think it would make more sense for a CollectionsMarshal overload as @Joe4evr suggested:

namespace System.Runtime.InteropServices
{
    public static class CollectionsMarshal
    {
        public static Span<T> AsSpan<T>(ImmutableArray<T>.Builder builder);
    }
}

I withdraw this proposal.

@alrz alrz closed this as completed Nov 3, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 6, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Collections
Projects
None yet
Development

No branches or pull requests

6 participants