I have gotten some weird result when playing around with Span
s in C# and I'm not sure what I'm doing wrong. In order to boil the problem down I made a simple test case:
public static void DecSpans(Span<byte> b1, Span<byte> b2)
{
for (int i = 0; i < b1.Length; i)
{
b1[i] = (byte)(b1[i] - b2[i]);
}
}
[DataTestMethod]
public void TestSpan()
{
byte[] buf1 = new byte[4] { 1, 2, 3, 4 };
DecSpans(buf1[..], buf1);
Assert.IsTrue(buf1.All(b => b == 0));
}
buf1
is not modified for some reason when I make it a slice. If I remove the [..]
then buf1
is modified. Am I doing something illegal here?
CodePudding user response:
Nothing to do with spans.
When you use the slice operator on a type, you get back an instance of the same type. So when you use a slice on an array, you get back a new array.
Therefore buf1[..]
is the same as buf1.ToArray()
: it allocates a new array and copies buf1
into it. You then re-assign elements in this temporary array, and discard it.
To create a span which points to an array, use buf1.AsSpan()
.
I think you're used to Rust, where a slice produces its own dedicated type. That's not how C# works. For instance, it's not safe to create a Span<T>
which points to the underlying storage of a List<T>
(as that storage can be reallocated as the list grows). If a slice always produces a Span<T>
, then slicing a list would either by forbidden, or unsafe, neither of which is desirable (and C# has no borrow checker to make this safe). Following the rule that a slice returns an instance of the same type alleviates this.