3.8 KiB
Ты абсолютно прав! Rust ощущается как "Lego Technic": детали (типы, трейты) либо идеально защелкиваются (type check passed), либо ты физически не можешь их соединить. В C++ часто можно "насильно" прикрутить деталь изолентой (reinterpret_cast), и она отвалится на ходу.
1. Как создавать методы для функциональных цепочек?
Да, ты можешь писать методы, которые "чейнятся" (chaining). Секрет в том, что метод должен:
- Принимать
self(по значению) — это называется builder pattern или consuming builder. - Возвращать
Self.
struct Config {
port: u16,
debug: bool,
}
impl Config {
fn new() -> Self {
Self { port: 80, debug: false }
}
// Принимает 'mut self', меняет его и возвращает 'Self'
fn set_port(mut self, port: u16) -> Self {
self.port = port;
self // Возвращаем измененный объект дальше по конвейеру
}
fn enable_debug(mut self) -> Self {
self.debug = true;
self
}
}
fn main() {
// Функциональная цепочка!
let conf = Config::new()
.set_port(8080)
.enable_debug();
}
Если ты про цепочки типа .map().filter(), то там возвращается не Self, а новый тип-обертка (например, Map<Iter>), который реализует тот же трейт Iterator.
2. Как работает магия collect()?
collect() — это, пожалуй, самый "волшебный" метод в Rust. Он умеет превращать итератор во что угодно, что реализует трейт FromIterator.
Сигнатура collect (упрощенно)
fn collect<B>(self) -> B
where
B: FromIterator<Self::Item>;
Он говорит: "Я могу стать типом B, если тип B умеет создавать себя из моих элементов".
Как это работает внутри?
Когда ты пишешь:
let vec: Vec<i32> = iter.collect();
- Rust видит, что ты хочешь получить
Vec<i32>. - Он ищет реализацию
impl FromIterator<i32> for Vec<i32>. - В этой реализации (в стандартной библиотеке) написано примерно следующее:
// Псевдокод реализации внутри std
impl<T> FromIterator<T> for Vec<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let mut vec = Vec::new();
for item in iter {
vec.push(item);
}
vec
}
}
То есть collect просто делегирует работу методу from_iter целевого типа.
Почему это круто?
Ты можешь реализовать FromIterator для своей структуры, и collect начнет работать для неё автоматически!
struct MyCollection(Vec<i32>);
// Учим нашу коллекцию создаваться из итератора
impl FromIterator<i32> for MyCollection {
fn from_iter<T: IntoIterator<Item = i32>>(iter: T) -> Self {
let mut c = MyCollection(Vec::new());
for i in iter {
c.0.push(i);
}
c
}
}
fn main() {
let my_col: MyCollection = vec![1, 2, 3].into_iter().collect(); // Работает!
}