Parse

Link:

Parse - don’t validate

Эта философия превращает подверженные ошибкам проверки в runtime в гарантии во время компиляции. То есть, входные данные в программе нельзя тащить дальше в код. Нужно их на входе проверять и отсеивать. Т.е. вопросы: “а вдруг там ничего нет void/none, а вдруг пользователь ввёл некорректные данные?” - надо решать сразу на входной функции чтения данных, и не тащить это по всей программе, везде делая реверанс в стиле “а если там в начале ничего не было, то… "

В Rust это можно и нужно зашивать в типы данных, которые гарантируют наличие контента.

Пример: непустой массив

Допустим, я создаю тип для дома, в котором будет массив комнат. Массив комнат в доме априори не может быть пустым - хотя бы одна комната должны быть!

Вариант 1 - валидация в конструкторе

pub struct House {
    rooms: Vec<Room>,
}
impl House {
    /// Конструктор, возвращающий Result - не может создать пустой дом
    pub fn new(rooms: Vec<Room>) -> Result<Self, &'static str> {
        if rooms.is_empty() {
            Err("House must have at least one room")
        } else {
            Ok(House { rooms })
        }
    }

Вариант 2 - NonEmpty

Добавляем зависимость cargo add nonempty спец-тип:

use nonempty::NonEmpty;

pub struct House {
    rooms: NonEmpty<Room>,  // Гарантированно не пустой список
}
impl House {
    /// Конструктор, принимающий как минимум одну комнату
    pub fn new(first_room: Room, rest_rooms: Vec<Room>) -> Self {
        let mut rooms = NonEmpty::new(first_room);
        rooms.extend(rest_rooms);
        House { rooms }
    }

Вариант 3 - собственный тип

Создаём собственный тип данных, аналог NonEmpty:

use std::ops::{Index, IndexMut};

/// Вектор, который гарантированно содержит хотя бы один элемент
#[derive(Debug, Clone)]
pub struct NonEmptyVec<T> {
    first: T,
    rest: Vec<T>,
}

impl<T> NonEmptyVec<T> {
    /// Создает новый NonEmptyVec с одним элементом
    pub fn new(first: T) -> Self {
        NonEmptyVec {
            first,
            rest: Vec::new(),
        }
    }
    
    /// Создает NonEmptyVec из Vec, возвращая None если вектор пуст
    pub fn from_vec(mut vec: Vec<T>) -> Option<Self> {
        if vec.is_empty() {
            None
        } else {
            let first = vec.remove(0);
            Some(NonEmptyVec {
                first,
                rest: vec,
            })
        }
    }
    
    /// Создает NonEmptyVec из Vec, паникуя если вектор пуст
    pub fn from_vec_unchecked(vec: Vec<T>) -> Self {
        assert!(!vec.is_empty(), "Cannot create NonEmptyVec from empty vector");
        let mut vec = vec;
        let first = vec.remove(0);
        NonEmptyVec {
            first,
            rest: vec,
        }
    }
    
    /// Создает NonEmptyVec из итератора, возвращая None если итератор пуст
    pub fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Option<Self> {
        let mut iter = iter.into_iter();
        let first = iter.next()?;
        let rest: Vec<T> = iter.collect();
        Some(NonEmptyVec { first, rest })
    }
    
    /// Возвращает количество элементов
    pub fn len(&self) -> usize {
        1 + self.rest.len()
    }
    
    /// Всегда возвращает false, так как NonEmptyVec никогда не пуст
    pub fn is_empty(&self) -> bool {
        false
    }
    
    /// Получает ссылку на элемент по индексу
    pub fn get(&self, index: usize) -> Option<&T> {
        if index == 0 {
            Some(&self.first)
        } else {
            self.rest.get(index - 1)
        }
    }
    
    /// Получает мутабельную ссылку на элемент по индексу
    pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
        if index == 0 {
            Some(&mut self.first)
        } else {
            self.rest.get_mut(index - 1)
        }
    }
    
    /// Возвращает ссылку на первый элемент
    pub fn first(&self) -> &T {
        &self.first
    }
    
    /// Возвращает мутабельную ссылку на первый элемент
    pub fn first_mut(&mut self) -> &mut T {
        &mut self.first
    }
    
    /// Возвращает ссылку на последний элемент
    pub fn last(&self) -> &T {
        self.rest.last().unwrap_or(&self.first)
    }
    
    /// Возвращает мутабельную ссылку на последний элемент
    pub fn last_mut(&mut self) -> &mut T {
        if self.rest.is_empty() {
            &mut self.first
        } else {
            self.rest.last_mut().unwrap()
        }
    }
    
    /// Добавляет элемент в конец
    pub fn push(&mut self, value: T) {
        self.rest.push(value);
    }
    
    /// Удаляет последний элемент, возвращая его
    /// Гарантированно возвращает Some, так как всегда есть хотя бы один элемент
    pub fn pop(&mut self) -> Option<T> {
        self.rest.pop().or_else(|| {
            // Не можем удалить последний элемент, так как это сделает коллекцию пустой
            // Вместо этого возвращаем None, сигнализируя, что удаление невозможно
            None
        })
    }
    
    /// Удаляет последний элемент, паникуя если это был последний элемент
    pub fn pop_unchecked(&mut self) -> T {
        self.rest.pop().expect("Cannot pop the last element of NonEmptyVec")
    }
    
    /// Вставляет элемент на указанную позицию
    pub fn insert(&mut self, index: usize, value: T) {
        if index == 0 {
            let old_first = std::mem::replace(&mut self.first, value);
            self.rest.insert(0, old_first);
        } else {
            self.rest.insert(index - 1, value);
        }
    }
    
    /// Удаляет элемент по индексу, возвращая его
    pub fn remove(&mut self, index: usize) -> Option<T> {
        if index == 0 {
            if self.rest.is_empty() {
                // Не можем удалить последний элемент
                None
            } else {
                let old_first = std::mem::replace(&mut self.first, self.rest.remove(0));
                Some(old_first)
            }
        } else {
            self.rest.remove(index - 1).into()
        }
    }
    
    /// Создает итератор
    pub fn iter(&self) -> impl Iterator<Item = &T> {
        std::iter::once(&self.first).chain(self.rest.iter())
    }
    
    /// Создает мутабельный итератор
    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
        std::iter::once(&mut self.first).chain(self.rest.iter_mut())
    }
    
    /// Преобразует в Vec
    pub fn into_vec(mut self) -> Vec<T> {
        let mut vec = Vec::with_capacity(self.len());
        vec.push(self.first);
        vec.append(&mut self.rest);
        vec
    }
    
    /// Применяет функцию ко всем элементам
    pub fn map<U, F>(self, mut f: F) -> NonEmptyVec<U>
    where
        F: FnMut(T) -> U,
    {
        NonEmptyVec {
            first: f(self.first),
            rest: self.rest.into_iter().map(f).collect(),
        }
    }
}

// Реализация Index для удобного доступа по индексу
impl<T> Index<usize> for NonEmptyVec<T> {
    type Output = T;
    
    fn index(&self, index: usize) -> &Self::Output {
        self.get(index).expect("Index out of bounds")
    }
}

// Реализация IndexMut для мутабельного доступа по индексу
impl<T> IndexMut<usize> for NonEmptyVec<T> {
    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
        self.get_mut(index).expect("Index out of bounds")
    }
}

// Реализация IntoIterator для использования в циклах
impl<T> IntoIterator for NonEmptyVec<T> {
    type Item = T;
    type IntoIter = std::vec::IntoIter<T>;
    
    fn into_iter(self) -> Self::IntoIter {
        self.into_vec().into_iter()
    }
}

// Реализация FromIterator для создания из итератора
impl<T> FromIterator<T> for NonEmptyVec<T> {
    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
        NonEmptyVec::from_iter(iter).expect("Cannot create NonEmptyVec from empty iterator")
    }
}