Search code examples
c#.net-7.0linqpad7

Getting a 'Use of unassigned local variable'in list pattern with variable with .NET 7


I have tested out list patterns in .NET 7 and C#. I am using Linqpad 7 and .NET 7.0.1. List patterns are useful I guess to compared sequences and fun to test out. The '_' discard here means to ignore the number at a given position and the '..' range here is to match anything between a given set of value and one index and then a given value at a higher index with arbitrary values between.

But in one of the samples, it says you can capture variables inside list patterns. I cannot make it work, I get a compiler error.

I am getting a CS0165 'Use of unassigned local variable' error when I try to access the variable(s) captured. I tried checking the crashing code also inside VsCode, still getting the error, however if I debug inside Linqpad I can see the variables that are captured got values at least.

    var someOddNumbers = new int[] { 1, 3, 5, 7, 9, 11 };
    bool resultX = someOddNumbers is [1, 3, _, _, _, 11];
    resultX.Dump("The 'someOddNumbers' equals a sequence of numbers 1,3,then three arbitrary numbers, then 11?");

    bool isOdd = someOddNumbers is [1, .., 9, 11];
    isOdd.Dump("The 'someOddNumbers' equals a sequence of numbers 1, some arbitrary numbers, then ending with 9 and 11?");
    
    result = input is [var firstOddNumber,.. , var lastOddNumber];

    if (result)
    {
        Console.WriteLine($"The captured variables are: {firstOddNumber} and {lastOddNumber}"); //this lines gives the CS0165 error
    }

If I comment out the if block I can run the code sample, and in the debugger I can see firstOddNumber and lastOddNumber being set to a value at runtime. But the C# 11 compiler seems to think this is illeagal code since it is using an unintialized variable.

Captured variables filled with value at runtime

I expected to not get a compiler error and be able to also capture the variables defined in the list pattern. I cannot understand the usage of such variables if I cannot use them. I understand that these variables might not be captured if the list pattern does not match, but even when checking if a match was present, I got the compilation error. I can however run the code, just not access the variables.

Update: A comma was missing before lastOddNumber as Guru Stron mentioned from my code. And using the list pattern inside a if condition made the code work.

var someOddNumbers = new int[] { 1, 3, 5, 7, 9, 11 };
bool resultX = someOddNumbers is [1, 3, _, _, _, 11];
resultX.Dump("The 'someOddNumbers' equals a sequence of numbers 1,3,then three arbitrary numbers, then 11?");

bool isOdd = someOddNumbers is [1, .., 9, 11];
isOdd.Dump("The 'someOddNumbers' equals a sequence of numbers 1, some arbitrary numbers, then ending with 9 and 11?");

if (someOddNumbers is [var firstOddNumber, .. ,  var lastOddNumber]){   
    Console.WriteLine($"The captured variables are: {firstOddNumber} and {lastOddNumber}");
}

Solution

  • It seems that compiler is not smart enough at the moment to determine that result is the match result and is not changed (or there are to many edge cases to implement such validation). Inlining the variable should make the code compiling:

    if (input is [var firstOddNumber,.. var lastOddNumber])
    {
        Console.WriteLine($"The captured variables are: {firstOddNumber} and {lastOddNumber}"); 
    }
    

    Small note:

    .. var lastOddNumber matches the rest of the array, not the last element, probably you were looking for is [var firstOddNumber, .., var lastOddNumber] (i.e. add comma after ..).

    UPD

    The same behaviour can be found with "ordinary" pattern matching:

    string? message = "This is not the null string";
    
    if (message is {} m)
    {
        Console.WriteLine(m); // compiles
    }
    
    var test = message is {} m1;
    if(test)
    {
       Console.WriteLine(m1); // error CS0165: Use of unassigned local variable 'm1'
    }
    

    This actually a known issue that compiler does not propagate the definite assignment state - see this and/or this.

    Demo.

    UPD

    The issue was closed by Jon Skeet with comment:

    Closing as "unfortunate, but compiler and spec agree."

    So for now this will not be implemented.