При последовательной обработке данных, когда для Result нужен and_then вместо map?
Используйте and_then, когда функция, применяемая к успешному значению, сама возвращает Result. Этот метод выполняет следующую fallible-операцию и не создаёт вложенный тип Result<Result<...>, E>.
map подходит, когда преобразование не может завершиться ошибкой и возвращает обычное значение. Оба метода пропускают замыкание при Err и сохраняют ошибку без изменений.
Тип Result позволяет описывать ожидаемые ошибки как часть контракта функции, не прибегая к исключениям. Однако последовательность fallible-операций быстро приводит к повторяющимся проверкам match.
Методы-комбинаторы вроде map и and_then были нужны для компактного построения цепочек преобразований с сохранением явной обработки ошибок. Их разделение отражает различие между обычным преобразованием значения и запуском следующей операции, которая тоже может завершиться ошибкой.
Предположим, сначала нужно разобрать строку, затем проверить полученное значение. Обе операции могут завершиться ошибкой, поэтому результат первой операции имеет тип Result<T, E>, а следующая функция возвращает Result<U, E>.
Если применить map, тип станет вложенным: Result<Result<U, E>, E>. Это усложняет дальнейшую обработку, поскольку внешний и внутренний уровни нужно разбирать отдельно; кроме того, цепочка перестаёт естественно прекращаться на первой ошибке.
map применяет функцию к значению внутри Ok и упаковывает возвращённое значение обратно в Ok. Концептуально преобразование имеет вид Result<T, E> в Result<U, E>, если функция принимает T и возвращает U.
and_then также запускает функцию только для Ok, но ожидает от неё уже Result<U, E>. Он возвращает этот результат напрямую, поэтому вложенность устраняется. Если исходное значение равно Err, функция не вызывается, а исходная ошибка передаётся дальше.
Минимальный пример:
Вызов load_port сначала пытается разобрать строку. При ошибке разбора проверка диапазона не запускается; при успешном разборе and_then передаёт число в validate_port и возвращает её Ok или Err без дополнительного уровня вложенности.
Главный компромисс — читаемость. Короткая цепочка and_then удобна для линейного конвейера, но при сложных ветвлениях обычный match может лучше показать бизнес-логику и позволить точнее преобразовать ошибки. and_then не обрабатывает ошибки автоматически: он лишь комбинирует значения Result; окончательное решение о возврате, логировании или преобразовании ошибки остаётся за вызывающим кодом.
Сервис получает строковый параметр порта из переменной окружения. Сначала значение нужно преобразовать в u16, затем проверить, что порт не равен нулю и соответствует политике приложения.
Вариант с несколькими вложенными match прозрачен и удобен, если для каждой ошибки требуется разное действие, но при простой линейной проверке он создаёт много структурного шума. Вариант с map приводит к вложенному Result, поэтому вызывающему коду приходится обрабатывать лишний уровень.
Выбран and_then, потому что обе операции возвращают Result, а требуемое поведение — остановиться на первой ошибке и сохранить единый тип результата. В итоге цепочка остаётся плоской, ошибки не теряются, а добавление ещё одной последовательной проверки не требует нового уровня вложенности.
Что произойдёт, если функция внутри map возвращает Result?
Результатом станет вложенный тип Result<Result<U, E2>, E1>, потому что map не умеет автоматически распрямлять результат функции. Это иногда бывает намеренным решением, например когда внешний и внутренний уровни означают разные этапы или разные классы ошибок, но для обычной последовательности fallible-операций чаще нужен and_then.
Вызывает ли and_then переданное замыкание при исходном Err?
Нет. Замыкание вызывается только для значения внутри Ok. Исходный Err возвращается без вызова следующей операции, поэтому and_then реализует короткое замыкание цепочки на первой ошибке.
Можно ли заменить and_then оператором ??
Да, в теле функции, которая возвращает совместимый Result, последовательность часто можно записать через локальные переменные и оператор ?. Такой вариант обычно удобнее, когда между шагами есть дополнительная логика, несколько значений или нужны промежуточные действия.
and_then лучше подчёркивает именно композицию функций и подходит для коротких цепочек выражений. Оператор ? не является полным аналогом комбинатора: он немедленно возвращает ошибку из текущей функции, тогда как and_then только строит новое значение Result внутри выражения.