Como posso iterar em um intervalo em Rust com uma etapa diferente de 1? Estou vindo de uma formação C ++, então gostaria de fazer algo como
for(auto i = 0; i <= n; i+=2) {
//...
}
Em Rust, preciso usar a range
função e não parece que haja um terceiro argumento disponível para ter uma etapa personalizada. Como posso fazer isso?
Parece-me que até que o
.step_by
método se torne estável, pode-se facilmente realizar o que você deseja com umIterator
(que é o queRange
realmente é de qualquer maneira):struct SimpleStepRange(isize, isize, isize); // start, end, and step impl Iterator for SimpleStepRange { type Item = isize; #[inline] fn next(&mut self) -> Option<isize> { if self.0 < self.1 { let v = self.0; self.0 = v + self.2; Some(v) } else { None } } } fn main() { for i in SimpleStepRange(0, 10, 2) { println!("{}", i); } }
Se for necessário iterar vários intervalos de diferentes tipos, o código pode ser genérico da seguinte maneira:
use std::ops::Add; struct StepRange<T>(T, T, T) where for<'a> &'a T: Add<&'a T, Output = T>, T: PartialOrd, T: Clone; impl<T> Iterator for StepRange<T> where for<'a> &'a T: Add<&'a T, Output = T>, T: PartialOrd, T: Clone { type Item = T; #[inline] fn next(&mut self) -> Option<T> { if self.0 < self.1 { let v = self.0.clone(); self.0 = &v + &self.2; Some(v) } else { None } } } fn main() { for i in StepRange(0u64, 10u64, 2u64) { println!("{}", i); } }
Vou deixar para você eliminar a verificação de limites superiores para criar uma estrutura aberta se um loop infinito for necessário ...
A vantagem dessa abordagem é que funciona com
for
adição de açúcar e continuará a funcionar mesmo quando recursos instáveis se tornarem utilizáveis; Além disso, ao contrário da abordagem sem açúcar usando os padrõesRange
, ele não perde eficiência com várias.next()
chamadas. As desvantagens são que são necessárias algumas linhas de código para configurar o iterador, portanto, pode valer a pena apenas para código que tem muitos loops.fonte
U
em sua segunda opção, você pode usar tipos que suportam adição com um tipo diferente e ainda produzir aT
. Por exemplo, tempo e duração vêm à mente.Use a caixa num com range_step
fonte
Você escreveria seu código C ++:
for (auto i = 0; i <= n; i += 2) { //... }
... em Rust assim:
let mut i = 0; while i <= n { // ... i += 2; }
Acho que a versão Rust também é mais legível.
fonte
continue
funcione corretamente. Embora possa ser feito, esse design incentiva a ocorrência de erros.Se você estiver avançando por algo predefinido e pequeno como 2, pode desejar usar o iterador para avançar manualmente. por exemplo:
let mut iter = 1..10; loop { match iter.next() { Some(x) => { println!("{}", x); }, None => break, } iter.next(); }
Você pode até usar isso para definir uma quantidade arbitrária (embora isso esteja definitivamente ficando mais longo e mais difícil de digerir):
let mut iter = 1..10; let step = 4; loop { match iter.next() { Some(x) => { println!("{}", x); }, None => break, } for _ in 0..step-1 { iter.next(); } }
fonte