Why does the following code not compile in C# 11?
// Example 1 - fails
class C {
public Span<int> M(ref int arg) {
Span<int> span;
span = new Span<int>(ref arg);
return span;
}
}
It produces two compile errors:
error CS9077: Cannot return a parameter by reference 'arg' through a ref parameter; it can only be returned in a return statement.
error CS8347: Cannot use a result of 'Span.Span(ref int)' in this context because it may expose variables referenced by parameter 'reference' outside of their declaration scope.
Neither of them makes sense to me: my code doesn't try to return arg
by a ref parameter, and it can't expose variables referenced by arg
outside of their declaration scope.
By comparison, the following two pieces of code compile successfully:
// Example 2 - succeeds
class C {
public Span<int> M(ref int arg) {
Span<int> span = new Span<int>(ref arg);
return span;
}
}
// Example 3 - succeeds
class C {
public Span<int> M(Span<int> arg) {
Span<int> span;
span = new Span<int>(ref arg[0]);
return span;
}
}
My intuition is that Span<int>
internally holds a ref field of type int
, so the escape rules should work the same for Examples 1 and 3 above (which, apparently, they do not).
I made an analogous experiment with a ref struct explicitly holding a ref field:
ref struct S {
public ref int X;
}
Now, the following code fails to compile:
// Example 4 - fails
class C {
public S M(ref int arg) {
S instance;
instance.X = ref arg;
return instance;
}
}
It produces the following error, which at least makes slightly more sense to me:
error CS9079: Cannot ref-assign 'arg' to 'X' because 'arg' can only escape the current method through a return statement.
By comparison, the following two pieces of code compile successfully (with the definition of S
above):
// Example 5 - succeeds
class C {
public S M(ref int arg) {
S instance = new S() { X = ref arg };
return instance;
}
}
// Example 6 - succeeds
class C {
public S M(S arg) {
S instance;
instance.X = ref arg.X;
return instance;
}
}
In particular, if arg
can only escape the current method through a return statement, as in the error message for Example 4 above, while doesn't the same hold for arg.X
in Example 6?
I tried to find the answer in the documentation for low level struct improvements, but I failed. Moreover, that documentation page seems to contradict itself in several places.
arg
in Example 4 is tighter than the "lifetime" ofarg.X
in Example 6, which I don't understand. – Pathogenicref
modifier on the parameter and therefore treats the parameter like a local variable instead of a returnable reference. – Roca