Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is ref struct in definition site

I think I've heard a term "ref like struct" in GitHub some time ago.

Now that I have my hands on latest C# version (7.3), I could finally test it my self. So this seems to be a valid code:

public ref struct MyStruct
{
    int x;
}

I know what are ref locals and ref returns as there is documentation about that. But I could not find documentation about ref struct.


Ref structs can not be used on auto properties or fields. They can not be cast to object either. These were empirical findings.

With "Span" background that new c# gave me recently I guessed that ref struct is a stack only struct. That is an struct that never goes on heap. But im not 100% sure.

Im pretty sure there should be a documentation about this but I failed to find it.

like image 288
M.kazem Akhgary Avatar asked Jan 12 '18 22:01

M.kazem Akhgary


People also ask

What is a ref struct?

What is a ref struct? Well, a ref struct is basically a struct that can only live on the stack. Now a common misconception is that since classes are reference types, those live on the heap and structs are value types and those live on the stack.

Is struct value or reference?

Structs are value types, while classes are reference types, and the runtime deals with the two in different ways. When a value-type instance is created, a single space in memory is allocated to store the value.

Can struct have reference types?

Yes they can. It depends. Many hold the stance that a struct should be immutable, and in this case, holding a reference to an object could mean it isn't.

What is ref class in C#?

The ref keyword indicates that a value is passed by reference. It is used in four different contexts: In a method signature and in a method call, to pass an argument to a method by reference. For more information, see Passing an argument by reference. In a method signature, to return a value to the caller by reference.


2 Answers

This addition to C# 7.2 is not really a feature in the sense of adding or enabling any new capability in the so-marked value-type itself, Rather, it allows the developer to declare or publish a specific restriction which governs the allowable use of that type everywhere else.

[edit: see span-safety at the github/dotnet site]

So instead of considering what ref struct designation gives the end-user of the struct, consider how it benefits the author. Adding any restriction on external use logically entails a related guarantee that the ref struct thus assumes, so the effect of the keyword is to empower or "license" the ref struct to do things that require those specific guarantees.

The point is that it's an indirect benefit, because the kinds of operations that are typically considered licensed by ref struct are basically none of the keyword's concern, and could be implemented and attempted, perhaps successfully even, by wily code anywhere, regardless of the ref struct marking (or not).

So much for the theoretical part. In reality, what is the "wily code" use-case that so existentially de­pends on the additional guarantees, even to the extreme point of accepting all the accompanying limitations? Essentially, it's the ability for a struct to expose a managed reference to itself or one of its fields.

Normally, C# enforces strong restrictions against the this reference leaking out of any instance method of a struct:

error CS8170: Struct members cannot return 'this' or other instance members by reference

The compiler has to be certain that there's virtually no possibility for this to leak out of the value-type because it's possible (in some uses, quite likely) that the struct instance has been temporarily boxed for the purpose of calling the instance method, in which case there would be no persistent GetPinnableReference instance relative to which a managed pointer to the struct (or its interior) might be taken.

With all the ref enhancements in recent years, C# now goes to even further lengths to detect and disallow this from escaping. For example, in addition to the above, we now have:

error CS8157: Cannot return 'x' by reference because it was initialized to a value that cannot be returned by reference

..and related errors such as...

error CS8374: Cannot ref-assign 'foo' to 'p' because 'foo' has a narrower escape scope than 'p'.

Sometimes the underlying reason for the compiler asserting CS8157 can be convoluted or hard to see, but the compiler hews to the conservative "better safe than sorry" approach, and this can some­times result in false positives where, for example, you have additional special knowledge that the escape is ultimately contained within the stack.

For cases where CS8157 is genuinely unwarranted (i.e., in consideration of information that the com­piler wasn't able to infer), these represent the "'perhaps even successful' wily code" examples I alluded to earlier, and generally there's no easy workaround, especially not via ref struct. That's because the convoluted false-positives often only arise in higher-level ref-passing code scenarios that would never be able to adopt the extreme limitations that are enforced for ref struct in the first place.

Instead, ref struct is used for very simple value-types. By guaranteeing them that their this ref­erence will always be anchored in an upper stack frame--and thus crucially, will never be awash in the GC heap--such types thus gain the confidence to publish managed pointers to themselves or their interiors.

Re­mem­ber, however, that I said ref struct is agnostic about how, why, and what the relaxations it provides are used for. What I was specifically alluding to there is that unfortunately, using ref struct does not make CS8157 go away (I consider this a bug, see here and here).

Because ref struct code that should properly be allowed to return its own this is still prevented by the compiler from doing so, you have to resort to some rather brutish techniques to get around the fatal error(s) when coding within the supposedly liberated ref struct instance methods. To wit, value-type instance methods written in C# that legitimately need to override fatal errors CS8170​/​CS8157 can opaque the 'this' pointer by round-tripping it through an IntPtr. This is left as an exercise for the reader, but one way to do this would be via the System.​Runtime.​CompilerServices.​Unsafe package.

like image 120
Glenn Slayden Avatar answered Nov 16 '22 03:11

Glenn Slayden


After some research, I stumbled upon this article on Compile time enforcement of safety for ref-like types in C# 7.2.

This C# feature is also known as “interior pointer” or “ref-like types”. The proposal is to allow the compiler to require that certain types such as Span<T> only appear on the stack.

The site also states the benefits of doing so, mainly concerning garbage collection and stack allocation.


Using ref-like types also brings some restrictions with it such as:

  • ref-like type cannot be a type of an array element
  • ref-like type cannot be used as a generic type argument
  • ref-like variable cannot be boxed
  • ref-like type cannot be a field of ordinary not ref-like type
  • ref-like types cannot implement interfaces
  • indirect restrictions, such as disallowed use of ref-like types in async methods, which are really a result of disallowing ref-like typed fields.

This limits them to be used for parameters, local variables, and in some cases return values.


There also exists an official documentation from Microsoft, as @UnholySheep pointed out in the comments.

like image 21
Ian H. Avatar answered Nov 16 '22 05:11

Ian H.