Replies: 13 comments 22 replies
-
Off the bat: ref structs implementing interfaces removes the need for duck typing, like |
Beta Was this translation helpful? Give feedback.
-
FYI if you're willing to operate on Enumerators directly, you can still get a lot of the benefit in .NET 9: using System;
using System.Collections;
using System.Collections.Generic;
List<int> list = [ 1, 2, 3];
Span<int> span = [ 1, 2, 3];
C.M(list.GetEnumerator());
C.M(span.GetEnumerator2());
public class C {
public static void M<TEnum>(TEnum enumerator)
where TEnum : IEnumerator<int>, allows ref struct
{
while (enumerator.MoveNext())
{
Console.WriteLine(enumerator.Current);
}
}
}
// Stand-in code until we implement IEnumerator for span enumerator
static class SpanExt
{
public static SpanEnum<T> GetEnumerator2<T>(this Span<T> span)
=> new SpanEnum<T>(span.GetEnumerator());
}
ref struct SpanEnum<T> : IEnumerator<T>
{
private readonly Span<T>.Enumerator _enum;
public SpanEnum(Span<T>.Enumerator e)
{
_enum = e;
}
public T Current => _enum.Current;
object IEnumerator.Current => _enum.Current!;
public void Dispose()
{
}
public bool MoveNext()
{
return _enum.MoveNext();
}
public void Reset()
{
throw new NotImplementedException();
}
} |
Beta Was this translation helpful? Give feedback.
-
One use case I've come across but never really needed that much is a way to pass a Span around that I can also be finished with early. I expect with C# 13 I could do something roughly like: interface ITemporarySpannable<T> : IDisposable
{
public Span<T> Span { get; }
}
explicit extension SpannableMemory<T> for Memory<T> : ITemporarySpannable<T>
{
// Memory<T> already has a Span property, not sure if we would need to redeclare it here?
public void Dispose() { } // no-op as this has no cleanup
}
explicit extension SpannableSelf<T> for Span<T> : ITemporarySpannable<T>
{
public Span<T> Span => return this;
public void Dispose() { } // no-op as this has no cleanup
}
ref struct ArrayLease<T> : ITemporarySpannable<T>
{
public ArrayLease(ArrayPool<T> pool, int size)
{
this.pool = pool;
this.array = pool.Rent(size);
}
T[] array;
ArrayPool<T> pool;
public Span<T> Span => array.AsSpan();
public void Dispose() => pool.Return(array); // return to pool when we are finished, even if still in scope.
// missing IsDisposed checks etc.
} This might be a dumb idea but I do keep finding myself wishing that ArrayPool was a little bit easier to work with than var/try/finally every time. |
Beta Was this translation helpful? Give feedback.
-
With |
Beta Was this translation helpful? Give feedback.
-
Here's my a use case for ref structs with interfaces: I'm working on an entity component system where I have my data in one struct
What I'd like to do is to make Component a |
Beta Was this translation helpful? Give feedback.
-
We need an new interface to carry the enumerator type instead of boxing it into interface IEnumerable<T, TEnum> where TEnum : IEnumerator<T>, allows ref struct
{
TEnum GetEnumerator();
}
ref struct Span<T> : IEnumerable<T, SpanEnumerator<T>>
{
SpanEnumerator<T> GetEnumerator() => new (this);
}
ref struct SpanEnumerator<T>(Span<T> span) : IEnumerator<T>
{
public T Current { get; }
public bool MoveNext();
} But this can be hard to use without associated types, ideally it should be: interface IEnumerable<T>
{
type TEnum : IEnumerator<T>, allows ref struct;
TEnum GetEnumerator();
}
ref struct Span<T> : IEnumerable<T>
{
type TEnum = SpanEnumerator<T>;
TEnum GetEnumerator() => new (this);
}
ref struct SpanEnumerator<T>(Span<T> span) : IEnumerator<T>
{
public T Current { get; }
public bool MoveNext();
} |
Beta Was this translation helpful? Give feedback.
-
For me, the ref struct implementing interfaces would be super nice for span/enumerables. That is my main use case. Maybe this was considered, but, perhaps instead of making spans implement interfaces, there should be a method to have the compiler generate a wrapper type that implements the interface. |
Beta Was this translation helpful? Give feedback.
-
I am wondering if it could be used to implement Lists with ref struct whose content is almost the same, only the Resize policy is different. |
Beta Was this translation helpful? Give feedback.
-
Worth to note that the runtime has added APIs to allow using ref structs for comparers, so that one can implement |
Beta Was this translation helpful? Give feedback.
-
I'm an author of https://github.com/U8String/U8String project (the one that uses cursed source generator api). Due to ever increasing amount of APIs, concerns regarding internal structure in combination with future extensibility and lack of time, the project has been progressing slowly. However, upon evaluating ref structs implementing interfaces and passed as generics, it appears that this exact feature will allow to unify and generalize significant amount of internal logic, bringing the approach closer to how Rust generalizes its own string implementations. Currently, With this change, I have a prototype that accepts |
Beta Was this translation helpful? Give feedback.
-
Bit of a niche use case but ...
[WithInterfaceProperties]
public readonly ref partial struct Inputs : IAreaThresholds, IDeviationThresholds, IMeanThresholds; vs ...
[WithInterfaceProperties<IAreaThresholds>]
[WithInterfaceProperties<IDeviationThresholds>]
[WithInterfaceProperties<IMeanThresholds>]
public readonly ref partial struct Inputs; |
Beta Was this translation helpful? Give feedback.
-
Thank you all so much! I refactored my HTML generation library to make heavy use of ref struct interfaces. In my case, all the HTML element types are The introduction of ref struct interfaces allows me to write code more generically. One example is the |
Beta Was this translation helpful? Give feedback.
-
In case it wasn't already brought up: imagine you have a lazy-instantiated collection, and a "proxy collection" to wrap around it: class Foo
{
public IList<string> Strings => this._list ??= [];
public Wrapper Numbers => new Wrapper(ref this._list);
List<string>? _list;
}
public readonly ref struct Wrapper : IEnumerable<int> // currently, this is CS8343
{
public Wrapper(ref List<string> list)
{
this._list = list;
}
public readonly void Add(int i)
{
(this._list ??= []).Add(i.ToString());
}
readonly ref List<string>? _list;
public IEnumerator<int> GetEnumerator() => throw new NotSupportedException();
IEnumerator IEnumerable.GetEnumerator() => throw new NotSupportedException();
}
var foo = new Foo
{
Strings = { "abc" },
Numbers = { 123 } // currently, this is CS1918
};
Console.WriteLine(string.Join(", ", foo.Strings));
// output: abc, 123 To accomplish something similar, I have to do some weird stuff with delegates and such. |
Beta Was this translation helpful? Give feedback.
-
https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-06-10.md
Agenda
ref struct
s implementing interfaces and in genericsThis agenda comes with an explicit request for you; yes, you reading this discussion! Do you have a use case for
ref struct
s implementing interfaces? If so, we want to hear about it, so we can ensure that we're building the right feature. Please let us hear your use cases!Beta Was this translation helpful? Give feedback.
All reactions