История вопроса
В Rust нет классических конструкторов как в ООП-языках, но есть ассоциированные функции (чаще всего new), которые реализуют паттерн конструкторов с явной инициализацией. Ассоциированные функции позволяют создавать экземпляры структур с предустановленными значениями или создавать их с дополнительной логикой (например, с резервированием памяти).
Проблема
Многие начинают использовать только new, забывая про другие паттерны фабричных методов (например, with_capacity), или неправильно реализуют инициализацию структур (особенно с приватными/публичными полями), что может привести к ошибкам инвариантности.
Решение
Ассоциированная функция типа new реализуется через impl, может быть единственной точкой входа для создания объектов с инвариантами. Функции вроде with_capacity дают дополнительные возможности, например, заранее аллоцировать память.
Пример кода:
pub struct MyBuffer { data: Vec<u8>, } impl MyBuffer { pub fn new() -> Self { MyBuffer { data: Vec::new() } } pub fn with_capacity(cap: usize) -> Self { MyBuffer { data: Vec::with_capacity(cap) } } }
Ключевые особенности:
Если не реализовать new, Rust создаст его по умолчанию?
Нет. Нет никакой автогенерации new. Если метод не реализован явно, его не будет, даже если структура имеет только публичные поля.
В чем принципиальная разница между new и Default?
Default — трейтовый конструктор, который может быть вызван только там, где тип реализует или явно унаследовал Default. Он стандартен для обобщенных алгоритмов, но не всегда совпадает с new.
Можно ли использовать методы new и with_capacity для приватных структур?
Да, методы можно сделать публичными или приватными, но создание публичного конструктора для приватной структуры не позволяет пользователям создавать экземпляры этой структуры за пределами модуля.
Открытая структура без инвариантов, все поля — публичные:
pub struct User { pub login: String, pub active: bool }
Плюсы:
Минусы:
Поля приватные, создание только через new, инварианты обеспечены:
pub struct User { login: String, active: bool } impl User { pub fn new(login: String) -> User { User { login, active: true } } }
Плюсы:
Минусы: