Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Использование анализатора заимствований для обеспечения инвариантов

Анализатор заимствований, добавленный для контроля владения памятью, может моделировать и другие задачи и предотвращать некорректное использование API.

// Copyright 2025 Google LLC
// SPDX-License-Identifier: Apache-2.0

/// Двери можно открывать и закрывать, и для запирания или отпирания нужен правильный ключ.
/// Смоделировано с помощью неизменяемой ссылки на ключ и владения дверью.
pub struct DoorKey {
    pub key_shape: u32,
}
pub struct LockedDoor {
    lock_shape: u32,
}
pub struct OpenDoor {
    lock_shape: u32,
}

fn open_door(key: &DoorKey, door: LockedDoor) -> Result<OpenDoor, LockedDoor> {
    if door.lock_shape == key.key_shape {
        Ok(OpenDoor { lock_shape: door.lock_shape })
    } else {
        Err(door)
    }
}

fn close_door(key: &DoorKey, door: OpenDoor) -> Result<LockedDoor, OpenDoor> {
    if door.lock_shape == key.key_shape {
        Ok(LockedDoor { lock_shape: door.lock_shape })
    } else {
        Err(door)
    }
}

fn main() {
    let key = DoorKey { key_shape: 7 };
    let closed_door = LockedDoor { lock_shape: 7 };
    let opened_door = open_door(&key, closed_door);
    if let Ok(opened_door) = opened_door {
        println!("Opened the door with key shape '{}'", key.key_shape);
    } else {
        eprintln!(
            "Door wasn't opened! Your key only opens locks with shape '{}'",
            key.key_shape
        );
    }
}
This slide and its sub-slides should take about 90 minutes.
  • Мы видели, как анализатор заимствований предотвращает ошибки безопасности памяти (использование после освобождения, гонки данных).

  • Мы также уже использовали типы для формирования и ограничения API с помощью паттерна Typestate.

  • Возможности языка часто вводятся с конкретной целью.

    Со временем пользователи могут находить способы использовать возможность, которые не предполагались при её введении.

    Java 5 ввела обобщения в 2004 году с основной заявленной целью — обеспечить типобезопасные коллекции.

    Сначала внедрение шло медленно, но некоторые новые проекты начали проектировать свои API вокруг обобщений с самого начала.

    С тех пор пользователи и разработчики языка расширили использование обобщений на другие области проектирования типобезопасных API:

    • Информацию о классе можно сохранить с помощью Class<T> в Java или TypeToken<T> из Guava.
    • Паттерн Builder можно реализовать с помощью рекурсивных обобщений. Здесь мы хотим сделать нечто похожее: хотя анализатор заимствований был введён для предотвращения использования после освобождения и гонок данных, мы будем рассматривать его как ещё один инструмент проектирования API. Его можно использовать для моделирования свойств программы, которые не имеют отношения к предотвращению ошибок безопасности памяти.
  • Чтобы использовать анализатор заимствований как инструмент решения задач, нужно «забыть», что его изначальная цель — предотвращать изменяемые псевдонимы в контексте использования после освобождения и гонок данных.

    Представьте, что работаем в ситуациях, где правила те же, но смысл немного другой.

  • В этом примере владение и заимствование используются для моделирования состояния физической двери.

    open_door потребляет LockedDoor и возвращает новое значение OpenDoor. Прежнее значение LockedDoor становится недоступно.

    Если используется неправильный ключ, дверь остаётся запертой. Она возвращается как вариант Err у Result.

    Попытка использовать дверь, которую уже открыли, — ошибка времени компиляции.

  • Аналогично, lock_door потребляет OpenDoor, поэтому закрыть дверь дважды невозможно — это проверяется на этапе компиляции.

  • Правила анализатора заимствований существуют для предотвращения ошибок безопасности памяти, но лежащая в их основе логическая система не «знает», что такое память.

    Анализатор заимствований лишь обеспечивает соблюдение определённого набора правил о том, в каком порядке пользователь может выполнять операции.

    Это лишь один из случаев, когда мы опираемся на правила анализатора заимствований, чтобы проектировать API, которые сложнее или невозможно использовать неправильно.