Шаблон Builder
Создавайте объекты шаг за шагом.
«Шаблон Builder» — бесплатный урок Learn Rust Coding на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Learn Rust Coding, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Learn Rust Coding содержит 4 уроков всего.
Зачем нужен Builder
В Rust нет именованных или необязательных аргументов функций. Когда у структуры много полей, особенно необязательных, конструктор с восемью позиционными параметрами становится нечитаемым и подверженным ошибкам.
Шаблон Builder решает эту проблему. Вы настраиваете объект пошагово с помощью именованных методов, а затем вызываете итоговый build(), чтобы получить значение. Такой код читается как связное предложение.
Целевая структура
Начните с типа, который действительно хотите создать. Здесь конфигурация сервера содержит обязательный хост и несколько необязательных параметров.
Обратите внимание: поля закрыты, чтобы поощрить создание через Builder, а не с помощью литералов структуры.
pub struct ServerConfig {
host: String,
port: u16,
max_connections: usize,
use_tls: bool,
}Отдельный тип Builder
Классический подход использует вторую структуру — Builder. Она повторяет целевую структуру, но хранит состояние процесса создания. Необязательные поля часто становятся Option<T>, чтобы можно было отличить состояние «не задано» от состояния «задано явно».
pub struct ServerConfigBuilder {
host: String,
port: Option<u16>,
max_connections: Option<usize>,
use_tls: bool,
}Начало работы с Builder
Создайте для Builder конструктор, принимающий только обязательные поля. Все необязательные поля изначально получают значение None или значение по умолчанию.
Распространённое соглашение — метод builder() у целевого типа, возвращающий Builder.
impl ServerConfig {
pub fn builder(host: impl Into<String>) -> ServerConfigBuilder {
ServerConfigBuilder {
host: host.into(),
port: None,
max_connections: None,
use_tls: false,
}
}
}Методы-сеттеры принимают self по значению
Каждый сеттер потребляет self, изменяет поле и возвращает self. Возврат значения, которым вы владеете, позволяет удобно объединять вызовы в цепочку.
Такая цепочка на основе владения — идиоматичный стиль Rust, который также избавляет от сложностей со временем жизни.
impl ServerConfigBuilder {
pub fn port(mut self, port: u16) -> Self {
self.port = Some(port);
self
}
pub fn use_tls(mut self, yes: bool) -> Self {
self.use_tls = yes;
self
}
}Применение значений по умолчанию в build()
Итоговый вызов build() превращает Builder в настоящий тип. Здесь для всех полей, которые всё ещё имеют значение None, подставляются значения по умолчанию с помощью unwrap_or.
impl ServerConfigBuilder {
pub fn build(self) -> ServerConfig {
ServerConfig {
host: self.host,
port: self.port.unwrap_or(8080),
max_connections: self.max_connections.unwrap_or(128),
use_tls: self.use_tls,
}
}
}Последовательное создание
Теперь процесс создания читается сверху вниз. Обязательные данные передаются в builder(), а каждая необязательная настройка задаётся вызовом с понятным именем.
Пропущенные поля без дополнительных действий получают значения по умолчанию.
fn main() {
let cfg = ServerConfig::builder("localhost")
.port(9000)
.use_tls(true)
.build();
println!("{}:{} tls={}", cfg.host, cfg.port, cfg.use_tls);
}Полный запускаемый пример
Здесь весь шаблон объединён в одну программу, которую можно запустить. Она показывает, что пропущенные поля получают значения по умолчанию внутри build().
struct Config { name: String, retries: u32 }
struct Builder { name: String, retries: Option<u32> }
impl Config {
fn builder(name: &str) -> Builder {
Builder { name: name.to_string(), retries: None }
}
}
impl Builder {
fn retries(mut self, n: u32) -> Self { self.retries = Some(n); self }
fn build(self) -> Config {
Config { name: self.name, retries: self.retries.unwrap_or(3) }
}
}
fn main() {
let c = Config::builder("job").build();
println!("{} retries={}", c.name, c.retries);
}Создание с обработкой ошибок через Result
Иногда конфигурация может быть недопустимой, например если порт равен нулю. Сделайте так, чтобы build() возвращал Result, тогда ошибки проверки будут проявляться как ошибки, которые можно обработать, а не как паники.
impl ServerConfigBuilder {
pub fn try_build(self) -> Result<ServerConfig, String> {
let port = self.port.unwrap_or(8080);
if port == 0 {
return Err("port must be non-zero".into());
}
Ok(ServerConfig { host: self.host, port,
max_connections: self.max_connections.unwrap_or(128),
use_tls: self.use_tls })
}
}Ящик derive_builder
Писать строители вручную — повторяющаяся работа. Ящик derive_builder генерирует весь строитель на основе аннотации.
Вы помечаете поля значениями по умолчанию и бесплатно получаете сгенерированный FooBuilder с методами-установщиками и build(), который может завершиться ошибкой.
use derive_builder::Builder;
#[derive(Builder)]
struct Channel {
#[builder(default = "8080")]
port: u16,
name: String,
}Строители, владеющие значением, и строители с изменяемой ссылкой
Существуют два стиля. Стиль с владением поглощает self и естественно поддерживает цепочки вызовов. Стиль с &mut self возвращает &mut Self и позволяет разбить построение на несколько операторов без повторного связывания переменной.
Стиль с владением чаще используют для одноразового создания объекта, а изменяемый стиль подходит для условной настройки в циклах.
impl ServerConfigBuilder {
pub fn port_ref(&mut self, port: u16) -> &mut Self {
self.port = Some(port);
self
}
}Быстрая проверка
Проверьте, насколько хорошо Вы поняли стиль строителя с владеющим self.
Итоги
Шаблон строителя компенсирует отсутствие необязательных аргументов в Rust. Тип строителя хранит состояние незавершённой работы, методы-установщики поглощают и возвращают self для построения цепочек вызовов, а build() применяет значения по умолчанию и создаёт итоговое значение.
Используйте Result из build() для проверки данных, а чтобы избежать шаблонного кода, обратитесь к derive_builder.
Часто задаваемые вопросы
Урок «Шаблон Builder» бесплатный?
Да — полный текст урока «Шаблон Builder» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Learn Rust Coding, подпишись на CoddyKit PRO. Курс Learn Rust Coding содержит 4 уроков всего.
Чему я научусь в уроке «Шаблон Builder»?
Создавайте объекты шаг за шагом. Ты практикуешь Learn Rust Coding с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Learn Rust Coding?
Предыдущий опыт не требуется. Learn Rust Coding на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Шаблон Builder»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Learn Rust Coding?
Да. Каждый урок Learn Rust Coding включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.