Pattern matching: срезы

Восполняя пробелы в изменениях Rust, которые происходили за время моего небольшого отсутствия, в одном из репортов заметил интересную вещь: стабилизацию #![feature(slice_patterns)] (Issue #320). Хотя образцы срезов были в языке уже довольно давно, но до этого моменты они были довольно ограничены. slice_patterns впервые появились еще в версии 1.26, и предоставили значительные улучшения, в частности стало удобнее работать с массивами переменной длины. Ниже разберем несколько примеров, которые теперь доступны в match.

Добавились два синтаксических шаблона: один для случаев, когда нужно привязать срез к переменной, другой для случаев, когда просто нужно указать пропущенные элементы. Оба шаблона используют .. (rest pattern) для соответствия переменному количеству элементов.

Сопоставления с привязкой элементов

Ниже представлена абстрактная функция с набором наиболее интересных шаблонов, конечно она не будет компилироваться!

fn foo(slice: &[&str]) {
    match slice {
        // сопоставление срезу любой длины
        // с привязкой первого и последнего элемента
        [x, .., y] => {
            println!("First: {:?} and last: {:?}.", x, y)
        }
        // сопоставление срезу с одним элементом
        [x] => {
            println!("slice has a single item: {:?}.", x)
        }
        // сопоставление срезу с тремя элементами
        [x, y, z] => {
            println!("{:?}, {:?}, {:?}", x, y, z)
        }
        // сопоставление с привязкой последнего элемента
        [.., x] => {
            println!("last {:?}", x)
        }
        // обязательно пустой шаблон, так как длина не известна
        _ => {} 
    }
}

Помните, что .. соответствует любому кол-ву элементов, включая 0 . Это означает, что первый шаблон соответствует любому срезу, который как минимум включает два элемента.

Сопоставления с привязкой подсреза

Привязка осуществляется с помощью оператора @.

fn first_and_sum(slice: &[i32]) {
    match slice {
        [] => {},
        [x, subslice @ ..] => {
            println!(
                "first {:?}, sum of the remaining elements {:?}",
                x,
                sum(subslice)
            )
        }
    }
}

В приведенном выше примере, если срез не пустой, будет взят первый элемент x, и посчитана сумма элементов остальной части среза subslice.

Другой пример - получение элемента в середине среза, при условии, что срез имеет нечетное количество элементов.

fn middle(slice: &[i32]) -> Option<i32> {
    match slice {
        [_, inner @ .., _] => middle(inner),
        [x] => Some(x),
        [] => None,
    }
}

Используя рекурсию, перебирается срез. Если срез длиннее 3 элементов, тогда пропускается один элемент в начале и один в конце, а срез оставшийся посередине используется в качестве входных данных для следующего шага.

Вывод

Мы разобрали далеко не все доступные функции, за более подробной информацией следует обратиться к Issue #320. slice_patterns невероятно годное дополнение к стандартному набору шаблонов и будет полезна всякому, кто сталкивается со срезами в повседневной работе.