Skip to main content
A ref struct is a custom value type in C# that is strictly constrained to be allocated only on the execution stack. The compiler enforces a rigorous set of rules to guarantee that instances of a ref struct can never escape to the managed heap, preventing them from being boxed, captured by closures, or embedded within heap-allocated objects.
Because standard struct types can be promoted to the heap (e.g., when boxed, when used as a field in a class, or when captured in an asynchronous state machine), the ref struct modifier was introduced to provide a compiler-enforced guarantee of stack-only semantics.

Compiler Constraints

To maintain the stack-only guarantee, the Roslyn compiler enforces the following structural and behavioral restrictions on any ref struct:
  • No Boxing: A ref struct cannot be cast to object, dynamic, or ValueType.
  • Interface Restrictions: While a ref struct can implement interfaces (as of C# 13), it cannot be cast or boxed directly to the interface type. It can only participate in interface dispatch through generic methods utilizing the allows ref struct anti-constraint.
  • Field Restrictions: A ref struct cannot be declared as a field within a class or a standard struct. It can only be a field within another ref struct.
  • No Closures: A ref struct cannot be captured by lambda expressions or local functions.
  • Asynchronous State Machines: A ref struct cannot be declared as a local variable in an async method if it is held across an await boundary, because the compiler rewrites async methods into state machine objects that reside on the heap.
  • Iterators: A ref struct cannot be held across a yield return boundary within an iterator method. As of C# 13, they are permitted as local variables in iterators as long as their lifetime does not span across the yield statement, which would require lifting them into a heap-allocated state machine.

ref Fields (C# 11+)

A ref struct is the only type in C# permitted to declare ref fields. A ref field stores a managed pointer (an interior reference) to data, rather than the data itself. This restriction exists for lifetime safety: it prevents heap-allocated objects from holding pointers to stack-allocated variables. If a heap object held a reference to a stack variable, that reference would become a dangling pointer as soon as the stack frame unwinds.

Generic Anti-Constraints (C# 13+)

Historically, a ref struct could not be used as a generic type argument because generic types do not guarantee stack-only allocation. Starting in C# 13, a ref struct can be passed as a generic type argument if the generic parameter explicitly declares the allows ref struct anti-constraint. This forces the generic type or method itself to adopt ref struct allocation rules.

readonly ref struct

A ref struct can be combined with the readonly modifier. This enforces immutability on the struct’s fields while maintaining the stack-only allocation constraints. In a readonly ref struct, all standard fields must be marked readonly. For ref fields, the field must be declared with the readonly modifier (readonly ref T). This ensures the reference itself cannot be reassigned to point to a different memory location. It does not require the referent (the underlying data) to be immutable, though you can optionally add readonly to the referent type (readonly ref readonly T) if strict deep immutability is required.
Tired of Poor C# Skills? Fix That With Deep Grasping!Learn More