Ref return doesn't work when struct is created in the method

Viewed 783

Why wouldn't this work?
I create the struct and want to return it by ref.

public readonly struct AuditResult
{
    public readonly bool AcceptChanges;
    public readonly string Reason;

    public AuditResult(bool acceptChanges, string reason)
    {
        AcceptChanges = acceptChanges;
        Reason = reason;
    }

    public static ref AuditResult AcceptAuditResult()
    {
        var auditResult = (new AuditResult(true, string.Empty));
        ref AuditResult res = ref auditResult;

        return ref res;
    }
}

With this error happens:

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

My variable is a ref in this case?

2 Answers

The C# documentation states that:

The return value must have a lifetime that extends beyond the execution of the method. In other words, it cannot be a local variable in the method that returns it. It can be an instance or static field of a class, or it can be an argument passed to the method. Attempting to return a local variable generates compiler error CS8168, "Cannot return local 'obj' by reference because it is not a ref local."

In order to use the return ref keyword, you will need to return an object that is not a local variable, as a local variable will go out of scope and be garbage collected. Instead, consider returning a reference to a member variable in your class/struct, as opposed to res. Also, consider whether you need to return the value by reference at all - if you are not accessing it somewhere else internally, there is no need to pass it by reference.


Note, however, that you can use the ref keyword locally to create aliases for local variable names, like so:

Foo a = new Foo();
ref Foo b = ref a;

Here, modifying b will also modify a. With this syntax, though, you can't pass local references outside of the current method scope.

This is a common error in non-managed languages like C++ or Rust - and by using ref semantics you are effectively writing code at the level when you have decided to manage your own memory.

Consider the following bit of C++:

char* Greet(char* name)
{
  char buffer[100];
  sprintf(buffer, "Hello, %s!", name);
  return buffer;
}

Once the method exits, buffer goes out out scope - it no longer exists, and you will be left with a dangling pointer. This pattern is called RAII - Resource acquisition is initialization.

You are trying to do something similar here -

ref var something = ...
return ref res;

That local variable goes out of scope - you'll be trying to return a reference to something that doesn't exist.

But then why does ref return exist in the first place?

Returning by reference means that only the reference is copied, not the struct. Sometimes, creating a new struct might be expensive.

static readonly AuditResult AcceptWithNoReason = 
    new AuditResult(true, string.Empty);

public static ref readonly AuditResult AcceptAuditResult()
{
    return ref AcceptWithNoReason; //this is valid
}

But you do have to return a reference to something that has a longer lifetime than that method call. Read more here.

Related