💡 Инструкция: Выберите один ответ из пяти. На 25 вопросов отведено 70 минут. В каждом вопросе верен только один вариант. После завершения вы увидите общий результат, результаты по 5 тематическим шкалам и подробные объяснения.
Вопрос 1 из 25
Какая реализация выражает механизм «Ленивость» без лишнего копирования?
Rust Rust · Ленивость Копировать
let mut calls = 0;
let iter = [1, 2, 3].iter().map(|n| { calls += 1; n * 2 });
println!("{calls}");
drop(iter);
Продублировать реализацию для каждого известного типа вокруг `calls`, вместо того чтобы исправить границы обобщения «Ленивость».
Клонировать захваченное состояние перед каждым вызовом `calls`, не уточняя требуемый трейт вызова или время жизни.
Добавить потребляющую операцию, если вычисление должно произойти; если нужны только побочные эффекты, явный for обычно читается честнее, чем map.
Собирать каждый промежуточный итератор в Vec, чтобы ленивость не влияла на порядок. Сигнатура при этом почти не меняется, однако исходное предусловие остаётся скрытым.
Заменить текущую форму `calls` на `Box<dyn Trait>` во всех местах, не проверяя владение, объектобезопасность и стоимость вызова.
Вопрос 4 из 25
Что проверяет `Vec::new`, если не расширять вывод за пределы выполненного пути?
Rust Rust · Ленивость Копировать
let mut seen = Vec::new();
let first = [1, 2, 3].iter().inspect(|n| seen.push(**n)).find(|n| **n == 2);
assert_eq!(first, Some(&2));
assert_eq!(seen, vec![1, 2]);
Диагностика инструмента сама однозначно подсказывает безопасное исправление, поэтому разбирать контракт отдельно не нужно.
find прекращает обход после совпадения, поэтому inspect не увидит третий элемент.
Если основной путь стабилен, ранний выход и повторный вход можно не воспроизводить отдельно. Контракт `iter` считался бы одинаковым при любом состоянии входа.
Отсутствие сообщения инструмента означает, что опасная ветвь была выполнена и признана корректной. Дополнительный сценарий вокруг `Vec::new` при таком толковании уже не требуется.
Если сценарий проходит в debug-сборке, в release и на другой целевой платформе результат будет тем же.
Вопрос 5 из 25
Что нужно пересмотреть в использовании `iterator_is_lazy`, чтобы код стал корректным?
Rust Rust · Тестирование и инструменты Cargo Копировать
#[test]
fn iterator_is_lazy() {
let calls = std::cell::Cell::new(0);
let it = [1, 2].iter().map(|x| { calls.set(calls.get() + 1); x * 2 });
assert_eq!(calls.get(), 0);
assert_eq!(it.collect::<Vec<_>>(), vec![2, 4]);
assert_eq!(calls.get(), 2);
}
Клонировать захваченное состояние перед каждым вызовом `iterator_is_lazy`, не уточняя требуемый трейт вызова или время жизни.
Для итератора проверять пустой ввод, частичное потребление, порядок элементов и число вызовов; не ограничиваться итоговым collect на одном обычном наборе.
Заменить текущую форму `iterator_is_lazy` на `Box<dyn Trait>` во всех местах, не проверяя владение, объектобезопасность и стоимость вызова.
Продублировать реализацию для каждого известного типа вокруг `iterator_is_lazy`, вместо того чтобы исправить границы обобщения «Тестирование и инструменты Cargo».
Собирать каждый промежуточный итератор в Vec, чтобы ленивость не влияла на порядок. Сигнатура при этом почти не меняется, однако исходное предусловие остаётся скрытым. В результате `iterator_is_lazy` считали бы безопасным и на путях, которых пример не выполняет.
Вопрос 10 из 25
Какая часть механизма «Адаптеры» зафиксирована тестом `copied`?
Rust Rust · Адаптеры Копировать
let out: Vec<_> = [1, 2, 3].iter().copied().map(|n| n + 1).collect();
assert_eq!(out, vec![2, 3, 4]);
Если сценарий проходит в debug-сборке, в release и на другой целевой платформе результат будет тем же.
copied переводит Iterator<Item=&i32> в Iterator<Item=i32>, после чего map работает со значениями без двойного разыменования.
Диагностика инструмента сама однозначно подсказывает безопасное исправление, поэтому разбирать контракт отдельно не нужно. Дополнительный сценарий вокруг `copied` при таком толковании уже не требуется.
Раз тест прошёл, производительность решения тоже можно считать подтверждённой. Дополнительный сценарий вокруг `copied` при таком толковании уже не требуется.
Результат можно считать доказательством свойства языка, даже если он зависит от конкретной библиотеки или платформы. Дополнительный сценарий вокруг `copied` при таком толковании уже не требуется.
Вопрос 15 из 25
Какая правка выдержит ревью контракта «Адаптеры»?
Rust Rust · Адаптеры Копировать
let values = [1, 2, 3, 4];
let sum: i32 = values.iter().filter(|n| **n % 2 == 0).map(|n| n * 10).sum();
println!("{sum}");
Строить цепочку из адаптеров с ясными этапами; при сложных ссылочных уровнях использовать copied или destructuring, чтобы не скрывать типы звёздочками.
Заменить текущую форму `values` на `Box<dyn Trait>` во всех местах, не проверяя владение, объектобезопасность и стоимость вызова.
Продублировать реализацию для каждого известного типа вокруг `values`, вместо того чтобы исправить границы обобщения «Адаптеры».
Добавить Clone ко всем trait bounds, даже если реализация использует только ссылки. При каждом таком вызове появляется отдельный владелец и оплачивается копирование данных. В результате `iter` считали бы безопасным и на путях, которых пример не выполняет.
Клонировать захваченное состояние перед каждым вызовом `values`, не уточняя требуемый трейт вызова или время жизни.
Вопрос 25 из 25
Как устранить исходную ошибку, связанную с `into_iter`?
Rust Rust · Итераторы Iterator Копировать
let mut it = vec![10, 20].into_iter();
println!("{:?} {:?} {:?}", it.next(), it.next(), it.next());
Сделать трейт объектобезопасным удалением всех методов, которые возвращают Self, без альтернативного API.
Принимать Iterator или IntoIterator по нужному контракту; не сохранять ссылку на временный итератор, который сразу уничтожается.
Клонировать захваченное состояние перед каждым вызовом `vec`, не уточняя требуемый трейт вызова или время жизни.
Продублировать реализацию для каждого известного типа вокруг `vec`, вместо того чтобы исправить границы обобщения «Итераторы Iterator».
Собирать каждый промежуточный итератор в Vec, чтобы ленивость не влияла на порядок. Сигнатура при этом почти не меняется, однако исходное предусловие остаётся скрытым. Из этого следовало бы, что иной порядок операций для `into_iter` ничего не меняет.