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

Атрибуты системы типов

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

Атрибут non_exhaustive

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

Он может применяться к структурам, перечислениям и вариантам перечислений.

Атрибут non_exhaustive использует синтаксис MetaWord и, следовательно, не принимает никаких входных данных.

Внутри определяющего крейта non_exhaustive не имеет эффекта.

#![allow(unused)]
fn main() {
#[non_exhaustive]
pub struct Config {
    pub window_width: u16,
    pub window_height: u16,
}

#[non_exhaustive]
pub struct Token;

#[non_exhaustive]
pub struct Id(pub u64);

#[non_exhaustive]
pub enum Error {
    Message(String),
    Other,
}

pub enum Message {
    #[non_exhaustive] Send { from: u32, to: u32, contents: String },
    #[non_exhaustive] Reaction(u32),
    #[non_exhaustive] Quit,
}

// Неисчерпывающие структуры могут быть созданы как обычно внутри определяющего крейта.
let config = Config { window_width: 640, window_height: 480 };
let token = Token;
let id = Id(4);

// Неисчерпывающие структуры могут быть сопоставлены исчерпывающе внутри определяющего крейта.
let Config { window_width, window_height } = config;
let Token = token;
let Id(id_number) = id;

let error = Error::Other;
let message = Message::Reaction(3);

// Неисчерпывающие перечисления могут быть сопоставлены исчерпывающе внутри определяющего крейта.
match error {
    Error::Message(ref s) => { },
    Error::Other => { },
}

match message {
    // Неисчерпывающие варианты могут быть сопоставлены исчерпывающе внутри определяющего крейта.
    Message::Send { from, to, contents } => { },
    Message::Reaction(id) => { },
    Message::Quit => { },
}
}

За пределами определяющего крейта типы, аннотированные non_exhaustive, имеют ограничения, которые сохраняют обратную совместимость при добавлении новых полей или вариантов.

Неисчерпывающие типы не могут быть созданы за пределами определяющего крейта:

Следующие примеры создания не компилируются за пределами определяющего крейта:

// Это типы, определённые в вышестоящем крейте, которые были аннотированы как
// `#[non_exhaustive]`.
use upstream::{Config, Token, Id, Error, Message};

// Нельзя создать экземпляр `Config`; если бы новые поля были добавлены в
// новой версии `upstream`, то это не скомпилировалось бы, поэтому
// это запрещено.
let config = Config { window_width: 640, window_height: 480 };

// Нельзя создать экземпляр `Token`; если бы новые поля были добавлены, то
// это больше не было бы unit-подобной структурой, поэтому константа с тем же именем,
// созданная из-за того, что это unit-подобная структура, не является публичной вне крейта;
// этот код не компилируется.
let token = Token;

// Нельзя создать экземпляр `Id`; если бы новые поля были добавлены, то
// сигнатура его функции-конструктора изменилась бы, поэтому его функция-конструктор
// не является публичной вне крейта; этот код не компилируется.
let id = Id(5);

// Можно создать экземпляр `Error`; введение новых вариантов не
// приведёт к тому, что это не скомпилируется.
let error = Error::Message("foo".to_string());

// Нельзя создать экземпляр `Message::Send` или `Message::Reaction`;
// если бы новые поля были добавлены в новой версии `upstream`, то это
// не скомпилировалось бы, поэтому это запрещено.
let message = Message::Send { from: 0, to: 1, contents: "foo".to_string(), };
let message = Message::Reaction(0);

// Нельзя создать экземпляр `Message::Quit`; если бы это было преобразовано в
// кортежный вариант перечисления в `upstream`, это не скомпилировалось бы.
let message = Message::Quit;

Существуют ограничения при сопоставлении с неисчерпывающими типами за пределами определяющего крейта:

  • При сопоставлении с образцом неисчерпывающего варианта (структуры или варианта перечисления) должен использоваться StructPattern, который должен включать ... Видимость конструктора кортежного варианта перечисления уменьшается до не более чем pub(crate).
  • При сопоставлении с образцом неисчерпывающего перечисления, сопоставление с вариантом не учитывается при исчерпываемости ветвей. Следующие примеры сопоставления не компилируются за пределами определяющего крейта:
// Это типы, определённые в вышестоящем крейте, которые были аннотированы как
// `#[non_exhaustive]`.
use upstream::{Config, Token, Id, Error, Message};

// Нельзя сопоставить неисчерпывающее перечисление без включения подстановочной ветки.
match error {
  Error::Message(ref s) => { },
  Error::Other => { },
  // скомпилировалось бы с: `_ => {},`
}

// Нельзя сопоставить неисчерпывающую структуру без подстановочного знака.
if let Ok(Config { window_width, window_height }) = config {
    // скомпилировалось бы с: `..`
}

// Нельзя сопоставить неисчерпывающую unit-подобную или кортежную структуру, кроме как с использованием
// синтаксиса структуры с фигурными скобками с подстановочным знаком.
// Это скомпилировалось бы как `let Token { .. } = token;`
let Token = token;
// Это скомпилировалось бы как `let Id { 0: id_number, .. } = id;`
let Id(id_number) = id;

match message {
  // Нельзя сопоставить неисчерпывающий вариант перечисления-структуры без включения подстановочного знака.
  Message::Send { from, to, contents } => { },
  // Нельзя сопоставить неисчерпывающий кортежный или unit вариант перечисления.
  Message::Reaction(type) => { },
  Message::Quit => { },
}

Также не разрешается использовать числовые приведения (as) для перечислений, которые содержат любые неисчерпывающие варианты.

Например, следующее перечисление может быть приведено, потому что оно не содержит неисчерпывающих вариантов:

#![allow(unused)]
fn main() {
#[non_exhaustive]
pub enum Example {
    First,
    Second
}
}

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

#![allow(unused)]
fn main() {
#[non_exhaustive]
pub enum EnumWithNonExhaustiveVariants {
    First,
    #[non_exhaustive]
    Second
}
}
use othercrate::EnumWithNonExhaustiveVariants;

// Ошибка: нельзя привести перечисление с неисчерпывающим вариантом, когда оно определено в другом крейте
let _ = EnumWithNonExhaustiveVariants::First as u8;

Неисчерпывающие типы всегда считаются населёнными (inhabited) в нижестоящих крейтах.