Skip to content

Instantly share code, notes, and snippets.

@jaredpar
Last active June 26, 2023 17:20
Show Gist options
  • Save jaredpar/a784f8963ce73613a0a696ef89a00d9a to your computer and use it in GitHub Desktop.
Save jaredpar/a784f8963ce73613a0a696ef89a00d9a to your computer and use it in GitHub Desktop.

Modeling a lifetime really comes down to answering two questions:

  1. What are the lifetime defaults for a scenario? Defaults when possible should lean into how the majority of developers are going to expect the code to work. The ideal outcome being that developers never realize lifetimes are even at play here.
  2. How does a developer invert a default when it does not work for their scenario?

For collection literals I believe we should embrace the following rules:

When a collection literal is used to initialize a ref struct local the value should not be returnable

The rationale being that developers using a ref struct which represents a collection is doing so for performance reasons. The compiler should lean into that by optimizing these scenarios as much as possible including leveraging stack allocation. This is the more natural interpretation of developers intent. If developers wanted to use a collection which was returnable then they would use a traditional collection type.

Span<int> M() {
    Span<int> span = [1, 2, 3];
    // Illegal
    return span;
}

In order to invert the default here developers can express their intent by using a traditional collection type

Span<int> M() {
    int[] array = [1, 2, 3];
    return array.AsSpan();
}

When a collection literal is used as a method argument the value should not be returnable from the current method of the following are true

  1. The parameter type is a ref struct and the parameter is scoped
  2. The parameter type is a ref struct and there are no ref struct return or ref / out / in parameters

For parameters intent is declared by the author of the method and that only comes from the scoped modifier. If that modifier is not present and the value has a path to be returned then the compiler must respect that and create a value that is returnable. Otherwise the compiler can opmtimize it.

For situations where this decision does not work the developer can include an explicit cast to force the collection literal to be unoptimized. For example G((int[])[1, 2, 3]).

In all other cases the compiler should emit expressions that have ref safe to escape of calling method (returnabel values).

Note: a future expansion of collection literals could consider allowing new to appear before a literal as a method of indicating "do not optimize this".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment