C
C#4w ago
peppy

Interaction of 'first class spans' with inline array types/Extension methods over inline array types

Given several inline array types of byte of various lengths used to model certain fixed size buffers:
[InlineArray(40)]
public struct ByteArray40 {
private byte _b;
}
[InlineArray(20)]
public struct ByteArray20 {
private byte _b;
}
// many, many others
[InlineArray(40)]
public struct ByteArray40 {
private byte _b;
}
[InlineArray(20)]
public struct ByteArray20 {
private byte _b;
}
// many, many others
would it be possible, with the upcoming 'first class spans' (https://github.com/dotnet/csharplang/issues/8714), to write an extension method over Span<byte> like such:
public static class Extensions {
public static string decode_name(this Span<byte> name) {
// ...
}
}
public static class Extensions {
public static string decode_name(this Span<byte> name) {
// ...
}
}
and then have that extension appear over all inline array types of byte as such:
public struct CHRDATA {
public ByteArray20 chr_name;
public ByteArray40 chr_name_extended;

public void test() {
string str_chr_name = chr_name.decode_name();
string str_chr_name_extended = chr_name_extended.decode_name();
}
}
public struct CHRDATA {
public ByteArray20 chr_name;
public ByteArray40 chr_name_extended;

public void test() {
string str_chr_name = chr_name.decode_name();
string str_chr_name_extended = chr_name_extended.decode_name();
}
}
or is that not covered by that proposal? If not, can this (defining a single extension method for any inline array of a given primitive) be achieved in some other way?
15 Replies
Unknown User
Unknown User4w ago
Message Not Public
Sign In & Join Server To View
333fred
333fred4w ago
There is no way that I can think of that this would work First class spans does not promote the inline array->span conversion to be part of the language
Petris
Petris4w ago
i thought the conversion already was a part of it?
333fred
333fred4w ago
Not in the same way Not "first class"
peppy
peppyOP4w ago
I see. Does expressing this 'explicitly' like this work or would this be invalid/UB/unsafe?
[InlineArray(20)]
public struct ByteArray20 {
private byte _b;
[UnscopedRef] public Span<byte> as_span => this;
}

[InlineArray(40)]
public struct ByteArray40 {
private byte _b;
[UnscopedRef] public Span<byte> as_span => this;
}

public static class Extensions {
public static string decode_name(this Span<byte> name) {
// ...
}
}

public struct CHRDATA {
public ByteArray20 chr_name;
public ByteArray40 chr_name_extended;

public void test() {
string str_chr_name = chr_name.as_span.decode_name();
string str_chr_name_extended = chr_name_extended.as_span.decode_name();
}
}
[InlineArray(20)]
public struct ByteArray20 {
private byte _b;
[UnscopedRef] public Span<byte> as_span => this;
}

[InlineArray(40)]
public struct ByteArray40 {
private byte _b;
[UnscopedRef] public Span<byte> as_span => this;
}

public static class Extensions {
public static string decode_name(this Span<byte> name) {
// ...
}
}

public struct CHRDATA {
public ByteArray20 chr_name;
public ByteArray40 chr_name_extended;

public void test() {
string str_chr_name = chr_name.as_span.decode_name();
string str_chr_name_extended = chr_name_extended.as_span.decode_name();
}
}
333fred
333fred4w ago
I think that's fine? Not 100%, but if you're not getting ref safety warnings then it's probably ok
peppy
peppyOP4w ago
As long as [UnscopedRef] is present I'm not getting any errors/warnings. I got the idea from a TerraFX snippet: https://discord.com/channels/143867839282020352/312132327348240384/1167843268231430154 But I have to admit I'm nowhere near familiar enough with how this all really works to tell at a glance.
333fred
333fred4w ago
One general bit of feedback is that you're really not following any C# naming guidelines here
peppy
peppyOP4w ago
Right. Apologies for the poor legibility. I happen to share this project with some Rust expats and we had one too many discussion about naming... And thanks for the pointers.
333fred
333fred4w ago
Unscoped ref here basically means, from a rust perspective, that you're returning something with the same lifetime as the thing you called it on IE, fn as_span<'a>(byteArray: &'a mut ByteArray20) -> &'a mut [byte] {} (assuming I remember my rust syntax right)
peppy
peppyOP4w ago
And the opposite (scoped?) would only have a lifetime of the actual as_span method call, meaning I can't return that ref because it would escape the method?
333fred
333fred4w ago
Right. The only valid things to return from there are &'static stuff The default (not UnscopedRef) isn't really the "opposite" It's more fn as_span<'a>(byteArray: &'a mut ByteArray20) -> &'static mut [byte] {}
peppy
peppyOP4w ago
I see. In the above example the unscoped ref is valid during test() and stops being valid thereafter?
333fred
333fred4w ago
Yep, just like in rust It's best to think of C# lifetime tracking as just rust lifetimes, with defaults implied for you
peppy
peppyOP4w ago
Right. I see. I'm personally not familiar with Rust, but I do like that I can now explain it to my collaborators as well in their terms- thank you very much for clarifying

Did you find this page helpful?