`ref` and `unsafe` in Async and Iterator Methods — Unlocking `Span<T>` in C# 13
Cristian Sifuentes

Cristian Sifuentes @cristiansifuentes

About: 🧠 Full-stack dev crafting scalable apps with [NET - Azure], [Angular - React], Git, SQL & extensions. Clean code, dark themes, atomic commits.

Joined:
Apr 15, 2025

`ref` and `unsafe` in Async and Iterator Methods — Unlocking `Span<T>` in C# 13

Publish Date: May 7
0 0

ref_and_unsafe_in_Async_and_Iterator_Methods

ref and unsafe in Async and Iterator Methods — Unlocking Span<T> in C# 13

Starting in C# 13, the language lifts one of its long-standing restrictions: the inability to use ref struct types, ref variables, or unsafe contexts in iterator (yield) or async methods.

This evolution is critical for developers working with performance-sensitive data, particularly through types like:

  • System.Span<T>
  • System.ReadOnlySpan<T>
  • Custom ref struct types

Let’s dive deep into what this change allows, what restrictions remain, and how it helps you write better, faster, and safer C# code.


Old Limitation: No ref in async or yield

Before C# 13, you couldn’t declare or use:

  • ref locals or ref struct variables in async methods
  • ref locals or ref struct variables in iterator methods
  • unsafe code blocks inside iterator methods

This made it impossible to use Span<T>, a high-performance, stack-only data structure, in any method that returned a Task, or used yield return.


What’s New in C# 13?

C# 13 partially lifts these restrictions:

  • ✅ You can declare ref locals and ref struct variables in async methods
  • ✅ You can use unsafe code in iterator methods
  • ⚠️ But you cannot access those ref locals across an await or yield return

This strikes a balance between safety and power. The compiler enforces boundaries to ensure memory safety.


Example: Using Span<T> in Async Method

public async Task ProcessAsync()
{
    ReadOnlySpan<byte> span = stackalloc byte[10];

    Console.WriteLine(span[0]);

    await Task.Delay(100); // 🔴 Can't access `span` after this
}
Enter fullscreen mode Exit fullscreen mode

✅ Compiles successfully

❌ But if you access span after await, the compiler produces an error.


Why These Restrictions?

ref struct types like Span<T> are stack-only. But:

  • await and yield break execution flow
  • They might resume later — on a different stack
  • That would make references to previous stack frames unsafe

So C# enforces:

Context Rule
async Can't use ref vars after await
iterator (yield) Can't use ref vars after yield
unsafe in iterator Allowed — but yield must be safe

Compiler Checks You Can Rely On

You’ll get CS4000-level compile-time errors if you violate the boundaries.

ref struct MyStruct { public int X; }

async Task Invalid()
{
    var val = new MyStruct();
    await Task.Yield();
    Console.WriteLine(val.X); // ❌ CS4015: Can't access ref struct here
}
Enter fullscreen mode Exit fullscreen mode

The compiler protects you from undefined behavior.


Best Use Cases

Scenario Benefit
Parsing binary data in async IO Use Span<T> for performance and safety
High-performance pipelines Write modern iterator-like APIs with stack safety
Unsafe algorithms in iterators Run efficient logic while yielding intermediate values
Custom low-level ref struct Unlocks new use cases with async-compatible parsing

Learn More


Final Thoughts

C# 13 opens the door for more safe, high-performance patterns, especially when working with memory-efficient types like Span<T>. By relaxing restrictions while preserving compiler-enforced safety, it empowers advanced developers to optimize data pipelines and I/O-heavy logic without sacrificing safety or structure.

Unlock the power of ref and Span<T> — even in your asynchronous workflows.


Written by: [Cristian Sifuentes] – Low-Level C# Engineer | Span Evangelist | .NET Runtime Tuner

Have you tried using Span<T> in async or iterator methods? Share your insights.

Comments 0 total

    Add comment