Skip to content

C# Ref Struct — Complete Guide

DodaTech Updated 2026-06-24 2 min read

In this tutorial, you'll learn about C# Ref Struct. We cover key concepts, practical examples, and best practices.

You create a small struct to hold temporary data, but it can still end up on the heap — boxed, as a field of a class, or in an array. ref struct guarantees the type lives only on the stack, never on the heap. This enables safe use of Span<T> and other stack-dependent types.

Wrong

public struct BufferWrapper
{
    public Span<byte> Data; // CS8345: Span cannot be a field of a non-ref-struct
}

Output: Compiler error CS8345. Span<T> can only be a field of a ref struct.

public ref struct BufferWrapper
{
    public Span<byte> Data { get; init; }
}

// Usage — only on the stack
Span<byte> buffer = stackalloc byte[256];
var wrapper = new BufferWrapper { Data = buffer };
Process(ref wrapper);

Output: Compiles. BufferWrapper is a stack-only type, so it can safely hold a Span<T>.

ref struct restrictions:

  • Cannot be boxed (no object, interface, dynamic)
  • Cannot be a field of a class or non-ref struct
  • Cannot be used in async methods, lambdas, or iterators
  • Cannot be a generic type argument
  • Can implement IDisposable (for using)
public ref struct Transformer
{
    private Span<byte> _buffer;

    public Transformer(Span<byte> buffer) => _buffer = buffer;

    public void Dispose() => _buffer.Clear();
}

Prevention

  • Use ref struct to hold Span<T> or ReadOnlySpan<T> fields.
  • Use ref struct for performance-sensitive temporary types.
  • Use readonly ref struct for immutable stack-only types.
  • Use ref struct with IDisposable for deterministic cleanup via using.
  • Use ref struct with scoped parameters to limit escape scope.
  • Avoid ref struct for types that need heap storage, async, or generics.

Common Mistakes with ref struct

  1. Misunderstanding that String is [Char] with poor performance for large text operations
  2. Using foldl instead of foldl' causing stack overflow on large lists
  3. Forgetting deriving (Show, Eq) on custom data types needed for debugging

These mistakes appear frequently in real-world CSHARP code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

Why can't ref structs be used in async methods?

Async methods can suspend execution and resume on a different thread. If a ref struct were on the stack when the method suspends, it would be lost. The compiler prevents this to avoid undefined behavior. Use Memory<T> instead for async scenarios.

Can ref structs implement interfaces?

No. A ref struct cannot implement interfaces because interface dispatch could cause boxing. They can, however, implement IDisposable specifically — the compiler has special handling for using with ref structs.

What is the scoped keyword used with ref structs?

scoped limits the escape scope of a parameter — it cannot be returned or stored beyond the method call. scoped ref struct parameters tell the caller the ref struct will not escape. This enables more efficient code generation.

ref struct is fundamental to Span<T> and is used extensively in Durga Antivirus Pro's zero-allocation parsing engine. For more C#, visit DodaTech.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro