моя контактная информация
Почтамезофия@protonmail.com
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Ржавчина Замыкание — это анонимная функция, которую можно сохранить в переменной или передать в качестве аргумента другим функциям. Вы можете создать замыкание в одном месте, а затем выполнять операции замыкания в другом контексте. В отличие от функций, замыкания позволяют захватывать значения в пределах области, в которой они определены.
Итератор отвечает за логику обхода каждого элемента последовательности и определение момента окончания последовательности. При использовании итераторов нам не нужно переопределять эту логику.
Замыкание — функциональная структура, которую можно сохранить в переменной.
В Rust замыкания — это анонимные функции, которые захватывают переменные из внешней среды. Поведение замыкания при захвате зависит от типов переменных и того, как замыкание использует эти переменные.
Захват по значению (По значению)
Когда замыкание захватывает переменную по значению, оно становится владельцем этой переменной. Это означает, что после создания замыкания исходную переменную больше нельзя использовать.
fn main() {
let text = "Hello".to_string();
// 使用 move 来显式地表示闭包将获取 text 的所有权
let closure = move || println!("{}", text);
// 这里 text 不能被使用,因为其所有权已经被闭包获取
// println!("{}", text); // 这将导致编译错误
closure(); // 打印 "Hello"
}
Захват по ссылке (По ссылке)
Когда замыкание захватывает переменную по ссылке, оно заимствует эту переменную. Это означает, что исходная переменная все еще доступна, но замыкание может только заимствовать ее, но не стать владельцем.
fn main() {
let text = "Hello";
// 闭包通过引用捕获 text
let closure = || println!("{}", text);
// text 仍然可用,因为它没有被移动
println!("{}", text); // 打印 "Hello"
closure(); // 再次打印 "Hello"
}
Изменяемый захват
Замыкание может захватывать изменяемую ссылку, позволяя ей изменять значение исходной переменной.
fn main() {
let mut count = 0;
// 闭包通过可变引用捕获 count
let mut closure = || {
count += 1; // 修改 count 的值
println!("Count: {}", count);
};
closure(); // 打印 "Count: 1"
closure(); // 打印 "Count: 2"
// count 的值现在是 2
println!("Final count: {}", count);
}
Если закрытие снято mut
Модификация, компиляция не удалась.
fn main() {
let mut count = 0;
// 闭包通过可变引用捕获 count
let closure = || {
count += 1; // 修改 count 的值
println!("Count: {}", count);
};
closure(); // 打印 "Count: 1"
closure(); // 打印 "Count: 2"
// count 的值现在是 2
println!("Final count: {}", count);
}
Сообщение об ошибке: Из-за заимствования переменной. count
,передача closure
Требуется изменяемая привязка.
Compiling playground v0.0.1 (/playground)
error[E0596]: cannot borrow `closure` as mutable, as it is not declared as mutable
--> src/main.rs:4:9
|
4 | let closure = || {
| ^^^^^^^ not mutable
5 | count += 1; // 修改 count 的值
| ----- calling `closure` requires mutable binding due to mutable borrow of `count`
...
8 | closure(); // 打印 "Count: 1"
| ------- cannot borrow as mutable
9 | closure(); // 打印 "Count: 2"
| ------- cannot borrow as mutable
|
help: consider changing this to be mutable
|
4 | let mut closure = || {
| +++
For more information about this error, try `rustc --explain E0596`.
error: could not compile `playground` (bin "playground") due to 1 previous error
завершение как аргумент
Замыкания можно использовать какПередача параметровДля других функций захватывайте переменные в среде.
fn main() {
// 创建一个整数变量
let number = 10;
// 创建一个闭包,它接受一个 i32 类型的参数并返回其平方
// 这里使用 || 表示这是一个闭包
let square = || number * number;
// 定义一个函数,它接受一个闭包作为参数并调用它
// 闭包作为参数需要指定其类型,这里使用 || -> i32 表示闭包没有参数并返回 i32 类型的值
fn call_closure<F>(f: F)
where
F: Fn() -> i32, // 使用 trait bound 指定闭包的签名
{
// 调用闭包并打印结果
let result = f();
println!("The result is: {}", result);
}
// 调用 `call_closure` 函数,并将闭包 `square` 作为参数传递
// 由于闭包 `square` 没有参数,我们可以直接传递
call_closure(square);
}
В этом примере:
square
, который захватывает по ссылке main
в функцииnumber
переменную и вычислите ее квадрат.call_closure
функция, которая принимаетFn() -> i32
Замыкания, связанные с признаками, принимают параметры. Это означает, что замыкание не принимает никаких параметров и возвращаетi32
тип значения.call_closure
функционировать и будетsquare
Замыкания передаются в качестве аргументов.потому чтоsquare
Замыкания не имеют параметров, их можно передавать непосредственно в качестве параметровcall_closure
。call_closure
Функция вызывает замыкание и выводит результат. Между функциями и замыканиями существует гораздо больше различий.Замыкания не всегда требуют чего-то вродеfn
Укажите типы параметров и возвращаемые значения, как у функций. Аннотации типов необходимы в функциях, поскольку они являются частью явного интерфейса, доступного пользователю. Важно строго определить эти интерфейсы, чтобы обеспечить единое понимание используемых функций и типов возвращаемых значений. Напротив, для таких открытых интерфейсов замыкания не используются: они хранятся в переменных и используются без их именования или предоставления доступа к ним пользователям библиотеки.
Замыкания обычно короткие и относятся к узкому контексту, а не к какой-либо произвольной ситуации. В этих ограниченных контекстах компилятор может надежно определить типы параметров и возвращаемых значений, аналогично тому, как он может определить типы большинства переменных (хотя бывают редкие случаи, когда компилятору требуются аннотации типа замыкания).
Подобно переменным, аннотации типов могут быть добавлены, если мы хотим повысить ясность и ясность, но недостатком является то, что код становится более многословным (в отличие от строго необходимого).
fn main() {
let a = 100;
let add_one = |x: i32| -> i32 { x + 1 };
let b = add_one(a);
println!("{}", b);
}
При использовании аннотаций типов синтаксис замыканий больше похож на синтаксис функций. Вот вертикальное сравнение определения функции, которая добавляет единицу к своему аргументу, и синтаксиса замыкания, имеющего такое же поведение. Здесь добавлено несколько пробелов для выравнивания соответствующих частей. Это показывает, насколько синтаксис замыкания похож на синтаксис функции, за исключением использования каналов и некоторого дополнительного синтаксиса:
fn add_one_v1 (x: u32) -> u32 { x + 1 }
let add_one_v2 = |x: u32| -> u32 { x + 1 };
let add_one_v3 = |x| { x + 1 };
let add_one_v4 = |x| x + 1 ;
Таким образом, приведенный выше пример можно упростить до:
fn main() {
let a = 100;
let add_one = |x| x + 1;
let b = add_one(a);
println!("{}", b);
}
Компилятор определяет конкретный тип для каждого параметра и возвращаемого значения в определении замыкания.
Давайте посмотрим на другой пример:
fn main() {
let a = 100i32;
let a1 = 100f32;
let closure = |x| x;
let b = closure(a);
let b1 = closure(a1);
println!("{}", b);
println!("{}", b1);
}
Обратите внимание, что это определение замыкания не добавляет никаких аннотаций типа, поэтому мы можем вызвать это замыкание с любым типом. Но если вы попытаетесь вызвать замыкание дважды, в первый раз с помощью i32, а второй раз с помощью f32, будет сообщено об ошибке:Это ожидаемо, поскольку ранее замыкание вызывалось с аргументом типа «i32».
Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
--> src/main.rs:6:22
|
6 | let b1 = closure(a1);
| ------- ^^ expected `i32`, found `f32`
| |
| arguments to this function are incorrect
|
note: expected because the closure was earlier called with an argument of type `i32`
--> src/main.rs:5:21
|
5 | let b = closure(a);
| ------- ^ expected because this argument is of type `i32`
| |
| in this closure call
note: closure parameter defined here
--> src/main.rs:4:20
|
4 | let closure = |x| x;
| ^
For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` (bin "playground") due to 1 previous error
Как только замыкание фиксирует ссылку на значение или владение им в среде, в которой оно определено (таким образом, влияя на то, что, если таковое имеется, перемещается в замыкание), код внутри тела замыкания определяет ссылку позже, когда замыкание Или как манипулируют значением (что влияет на то, что будет удалено из замыкания). Тело замыкания может выполнять любое из следующих действий: перемещать захваченное значение из замыкания, изменять захваченное значение, не перемещать и не изменять значение или вообще никогда не извлекать значение из среды.
То, как замыкание фиксирует и обрабатывает значения в среде, влияет на характеристики, реализуемые замыканием. Признаки — это способ для функций и структур указать типы замыканий, которые они могут использовать.В зависимости от того, как тело замыкания обрабатывает значения, замыкание автоматически и постепенно реализует одно, два или три значения.Fn
черта.
FnOnce
Применяется к замыканиям, которые можно вызвать один раз. Все замыкания реализуют по крайней мере эту особенность, поскольку все замыкания могут быть вызваны.Замыкание, которое перемещает захваченные значения из тела замыкания, реализует толькоFnOnce
черта, потому что ее можно вызвать только один раз.FnMut
Подходит для замыканий, которые не перемещают захваченное значение из тела замыкания, но могут изменять захваченное значение. Этот тип замыкания можно вызывать несколько раз.Fn
Применяется к замыканиям, которые не перемещают захваченное значение из тела замыкания и не изменяют захваченное значение и, конечно же, включают замыкания, которые не извлекают значение из среды. Такие замыкания можно вызывать несколько раз без изменения их среды, что важно в сценариях, где замыкания вызываются несколько раз одновременно.Вот пример, показывающий, как использовать его в функции FnOnce
в качестве общего ограничения и убедитесь, что замыкание вызывается только один раз:
fn call_once<F, T>(f: F) -> T
where
F: FnOnce() -> T, // 约束 F 为 FnOnce trait,意味着它接受一个空参数并返回 T 类型
{
f() // 调用闭包并返回结果
}
fn main() {
// 创建一个闭包,它捕获了 `value` 的所有权
let value = 42;
let consume = move || {
let result = value; // 移动 `value`
println!("The value is: {}", result);
result // 返回结果
};
// 调用 `call_once` 函数,传入闭包
let result = call_once(consume);
println!("Result of the closure: {}", result);
// 尝试再次使用 `consume` 将会导致编译错误,因为它已经消耗了 `value`
// call_once(consume);
}
результат операции
The value is: 42
Result of the closure: 42
В этом примере мы определяем общую функцию call_once
, который принимает тип F
Параметрыf
,в F
Должно быть достигнутоFnOnce
черта.это означаетf
это замыкание, которое принимает пустые параметры и возвращает типT
результат.
существовать main
В функции мы создаем замыканиеconsume
, который фиксирует value
собственности.Затем мы вызываемcall_once
функция, переданная вconsume
Закрытие.call_once
Функция вызывает замыкание и возвращает его результат.потому чтоconsume
Закрытие было израсходованоvalue
, попробуйте позвонить еще раз call_once
Передача того же замыкания приведет к ошибке компиляции.
Вот использование FnMut
Примеры черт.
fn apply_mut<F, T>(func: &mut F, num: i32) -> T
where
F: FnMut(i32) -> T, // F 是一个可变闭包,接受一个 i32 类型的参数并返回类型为 T 的结果
{
func(num) // 调用闭包并返回结果
}
fn main() {
let mut count = 0;
// 创建一个闭包,它接受一个 i32 类型的参数并将其加到 count 上
let mut increment = |num: i32| -> i32 {
count += num;
count
};
// 使用 apply_mut 函数和 increment 闭包的引用
let result: i32 = apply_mut(&mut increment, 5);
println!("Result after applying increment: {}", result);
// 再次使用 apply_mut 函数和 increment 闭包的引用
let result: i32 = apply_mut(&mut increment, 10);
println!("Result after applying increment again: {}", result);
}
результат операции
Result after applying increment: 5
Result after applying increment again: 15
В этом примереapply_mut
Функция принимаетF
изменяемая ссылка типа&mut F
в качестве параметра (так,increment
Закрытие не будет перемещаться и может использоваться несколько раз) иi32
параметры типаnum
。where
пункт определяетF
Должна быть реализацияFnMut
закрытие, которое принимаетi32
тип параметров и возвращает типT
результат.main
Изменяемое замыкание создается в функцииincrement
, который изменяет захваченную переменную count
, затем используйте apply_mut
функция для вызова этого замыкания.каждый звонокapply_mut
Буду использоватьincrement
закрытие, иincrement
Замыкания будут измененыcount
ценить.
В Русте,Fn
Эта черта означает, что замыкание не становится владельцем переменных, которые оно захватывает, и не изменяет эти переменные.В следующем примере показано, как определить AcceptFn
Замыкание в качестве аргумента функции и использование его в параллельных сценариях.
use std::thread;
// 定义一个函数,它接受一个实现了 Fn(i32) -> i32 的闭包,并调用它
fn call_once<F, T>(func: F) -> T
where
F: Fn(i32) -> T, // 指定 F 是一个接受 i32 并返回 T 的 Fn 闭包
{
let result = func(42); // 调用闭包,传入一个 i32 类型的值
result // 返回闭包的执行结果
}
fn main() {
// 定义一个简单的 Fn 闭包,它接受一个 i32 类型的参数并返回两倍的该值
let double = |x: i32| -> i32 {
x * 2
};
// 创建多个线程,每个线程都使用相同的闭包
let handles: Vec<_> = (0..5).map(|i| {
let func = double; // 闭包可以被复制,因为它是 Fn 类型的
thread::spawn(move || {
let result = call_once(func); // 调用 call_once 函数,并传入闭包
println!("Thread {} result: {}", i, result); // 打印结果
})
}).collect();
// 等待所有线程完成
for handle in handles {
handle.join().unwrap();
}
}
Не каждый запуск выполняется в таком порядке. Какой поток запускается первым, зависит от текущего планирования системы.
результат операции
Thread 1 result: 84
Thread 2 result: 84
Thread 0 result: 84
Thread 4 result: 84
Thread 3 result: 84
В этом примере:
call_once
функция, которая принимает общий параметрF
,и F
должен быть удовлетворенFn(i32) -> T
границы черт.это означаетF
это замыкание, которое принимаетi32
тип параметров и возвращает типT
результат.main
В функции мы определяем простое замыканиеdouble
, который принимает i32
параметры типаx
и вернутьсяx * 2
результат.map
Создается 5 тредов и каждый тред копируетсяdouble
Закрытие и вызов в новом потокеcall_once
функция, передавая замыкание в качестве аргументаcall_once
。call_once
Вызывается функция, выполняется замыкание и выводится результат.join
Метод ожидает завершения всех потоков.Итераторы позволяют выполнять определенную обработку элементов последовательности. Далее мы в основном знакомим с использованием итераторов для обработки последовательностей элементов и сравнением производительности циклов и итераторов. В Rust итераторы являются ленивыми, что означает, что никакая операция не выполняется до тех пор, пока не будет вызван метод, использующий итератор.
Все итераторы реализуют функцию под названием Iterator
определяется в свойстве стандартной библиотеки. Определение этой черты выглядит следующим образом:
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
// 此处省略了方法的默认实现
}
type Item
иSelf::Item
, они определяют связанный тип признака.Но сейчас все, что вам нужно знать, это то, что этот код показывает, как реализоватьIterator
Черты требуют, чтобыItem
типа, этоItem
тип используется какnext
Тип возвращаемого значения метода. другими словами,Item
Тип будет типом элемента, возвращаемого итератором.
next
даIterator
Единственные разработчики метода должны определить.next
Возвращает по одному элементу в итераторе, инкапсулированном вSome
, когда итератор заканчивается, он возвращаетNone
。
Iterator
черты имеют набор различных методов, которые по умолчанию реализованы в стандартной библиотеке;Iterator
Все эти методы можно найти в документации API стандартной библиотеки типажа.Некоторые методы вызывают свое определениеnext
метод, поэтому при реализацииIterator
черта должна быть реализованаnext
причины метода.
Эти звонки next
Методы методов называются потребляющими адаптерами, поскольку при их вызове используются итераторы.
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
// 使用into_iter()将Vec转换为消费迭代器
let numbers_iter = numbers.iter();
let mut sum: i32 = numbers_iter
// 使用sum适配器计算迭代器中所有元素的总和
.sum();
// 打印总和
println!("The sum is: {}", sum);
}
Если вы воспользуетесь им снова numbers_iter
Будет сообщено об ошибке.sum
Метод берет на себя ответственность за итератор и вызывает его повторно.next
для перебора итератора, тем самым потребляя итератор. Обходя каждый элемент, он добавляет каждый элемент к сумме и возвращает сумму после завершения итерации.передачаsum
Больше нельзя использоватьnumbers_iter
, потому что звоню sum
когда он становится владельцем итератора.
Iterator
Другой класс методов, определенных в типажах, называемый адаптерами итераторов, позволяет нам изменить текущий итератор на итератор другого типа. Несколько адаптеров итераторов могут быть вызваны в цепочке. Однако, поскольку все итераторы являются ленивыми, необходимо вызвать метод потребляющего адаптера, чтобы получить результаты вызова адаптера итератора.
fn main() {
let v1: Vec<i32> = vec![1, 2, 3];
let v2: Vec<_> = v1.iter().map(|x| x * x).collect();
assert_eq!(v2, vec![1, 4, 9]);
}
Метод сбора использует итератор и собирает результаты в структуру данных.потому чтоmap
Получает замыкание, позволяющее указать любые операции, которые необходимо выполнить над каждым элементом, через который оно проходит.
Многие адаптеры итераторов принимают замыкания в качестве параметров, и обычно замыкание, указанное в качестве параметра адаптера итератора, будет замыканием, фиксирующим его среду.
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
// 使用into_iter()将Vec转换为消费迭代器
let filtered_and_squared: Vec<i32> = numbers.into_iter()
// 使用filter适配器过滤元素,接受一个闭包作为参数
// 闭包捕获其环境,这里指numbers的元素
.filter(|&x| x % 2 == 0) // 保留偶数
// 使用map适配器对过滤后的元素进行变换,也接受一个闭包
.map(|x| x * x) // 对每个元素进行平方
// 使用collect适配器将结果收集到一个新的Vec中
.collect();
// 打印过滤和平方后的结果
println!("The filtered and squared numbers are: {:?}", filtered_and_squared);
}
результат операции
The filtered and squared numbers are: [4, 16]
В этом примереfilter
иmap
Оба являются адаптерами итераторов, они принимают замыкания в качестве параметров. Эти замыкания захватывают свое окружение, то есть элементы итератора.существоватьfilter
В адаптере, закрытие|&x| x % 2 == 0
Используется для проверки того, является ли элемент четным числом. Если да, то элемент будет сохранен.существоватьmap
В адаптере, закрытие|x| x * x
Используется для придания квадратуры каждому элементу. Поскольку эти адаптеры являются потребительскими, они используют исходный итератор и поэтому не могут быть использованы снова. наконец,collect
Адаптер собирает обработанные элементы в новыйVec
середина.
Итераторы, как абстракция высокого уровня, компилируются в код с примерно такой же производительностью, как и написанный вручную низкоуровневый код. Итераторы — это одна из абстракций с нулевой стоимостью в Rust, что означает, что абстракция не приводит к накладным расходам во время выполнения.
fn main() {
let numbers1 = (0..1000000).collect::<Vec<i64>>();
let numbers2 = (0..1000000).collect::<Vec<i64>>();
let mut sum1 = 0i64;
let mut sum2 = 0i64;
// 测量for循环的性能
let start = std::time::Instant::now();
for val in numbers1 {
sum1 += val;
}
let loop_duration = start.elapsed();
// 测量迭代器的性能
let start = std::time::Instant::now();
for val in numbers2.iter() {
sum2 += val;
}
let iterator_duration = start.elapsed();
println!("Iterator took: {:?}", iterator_duration);
println!("For loop took: {:?}", loop_duration);
}
результат операции
Iterator took: 12.796012ms
For loop took: 11.559512ms
Iterator took: 12.817732ms
For loop took: 11.687655ms
Iterator took: 12.75484ms
For loop took: 11.89468ms
Iterator took: 12.812022ms
For loop took: 11.785106ms
Iterator took: 12.78293ms
For loop took: 11.528941ms
Из этого примера видно, что итераторы все еще немного медленнее.