Skip to content

Commit 422ebd5

Browse files
authored
Auto merge of #33312 - Byron:double-ended-iterator-for-args, r=alexcrichton
DoubleEndedIterator for Args This PR implements the DoubleEndedIterator trait for the `std::env::Args[Os]` structure, as well as the internal implementations. It is primarily motivated by me, as I happened to implement a simple `reversor` program many times now, which so far had to use code like this: ```Rust for arg in std::env::args().skip(1).collect::<Vec<_>>().iter().rev() {} ``` ... even though I would have loved to do this instead: ```Rust for arg in std::env::args().skip(1).rev() {} ``` The latter is more natural, and I did not find a reason for not implementing it. After all, on every system, the number of arguments passed to the program are known at runtime. To my mind, it follows KISS, and does not try to be smart at all. Also, there are no unit-tests, primarily as I did not find any existing tests for the `Args` struct either. The windows implementation is basically a copy-pasted variant of the `next()` method implementation, and I could imagine sharing most of the code instead. Actually I would be happy if the reviewer would ask for it.
2 parents edecc57 + 1aa8dad commit 422ebd5

File tree

4 files changed

+76
-10
lines changed

4 files changed

+76
-10
lines changed

src/libstd/env.rs

+11
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,13 @@ impl ExactSizeIterator for Args {
625625
fn len(&self) -> usize { self.inner.len() }
626626
}
627627

628+
#[stable(feature = "env_iterators", since = "1.11.0")]
629+
impl DoubleEndedIterator for Args {
630+
fn next_back(&mut self) -> Option<String> {
631+
self.inner.next_back().map(|s| s.into_string().unwrap())
632+
}
633+
}
634+
628635
#[stable(feature = "env", since = "1.0.0")]
629636
impl Iterator for ArgsOs {
630637
type Item = OsString;
@@ -637,6 +644,10 @@ impl ExactSizeIterator for ArgsOs {
637644
fn len(&self) -> usize { self.inner.len() }
638645
}
639646

647+
#[stable(feature = "env_iterators", since = "1.11.0")]
648+
impl DoubleEndedIterator for ArgsOs {
649+
fn next_back(&mut self) -> Option<OsString> { self.inner.next_back() }
650+
}
640651
/// Constants associated with the current target
641652
#[stable(feature = "env", since = "1.0.0")]
642653
pub mod consts {

src/libstd/sys/unix/os.rs

+4
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,10 @@ impl ExactSizeIterator for Args {
317317
fn len(&self) -> usize { self.iter.len() }
318318
}
319319

320+
impl DoubleEndedIterator for Args {
321+
fn next_back(&mut self) -> Option<OsString> { self.iter.next_back() }
322+
}
323+
320324
/// Returns the command line arguments
321325
///
322326
/// Returns a list of the command line arguments.

src/libstd/sys/windows/os.rs

+17-10
Original file line numberDiff line numberDiff line change
@@ -278,23 +278,30 @@ pub struct Args {
278278
cur: *mut *mut u16,
279279
}
280280

281+
unsafe fn os_string_from_ptr(ptr: *mut u16) -> OsString {
282+
let mut len = 0;
283+
while *ptr.offset(len) != 0 { len += 1; }
284+
285+
// Push it onto the list.
286+
let ptr = ptr as *const u16;
287+
let buf = slice::from_raw_parts(ptr, len as usize);
288+
OsStringExt::from_wide(buf)
289+
}
290+
281291
impl Iterator for Args {
282292
type Item = OsString;
283293
fn next(&mut self) -> Option<OsString> {
284-
self.range.next().map(|i| unsafe {
285-
let ptr = *self.cur.offset(i);
286-
let mut len = 0;
287-
while *ptr.offset(len) != 0 { len += 1; }
288-
289-
// Push it onto the list.
290-
let ptr = ptr as *const u16;
291-
let buf = slice::from_raw_parts(ptr, len as usize);
292-
OsStringExt::from_wide(buf)
293-
})
294+
self.range.next().map(|i| unsafe { os_string_from_ptr(*self.cur.offset(i)) } )
294295
}
295296
fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
296297
}
297298

299+
impl DoubleEndedIterator for Args {
300+
fn next_back(&mut self) -> Option<OsString> {
301+
self.range.next_back().map(|i| unsafe { os_string_from_ptr(*self.cur.offset(i)) } )
302+
}
303+
}
304+
298305
impl ExactSizeIterator for Args {
299306
fn len(&self) -> usize { self.range.len() }
300307
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use std::env::args;
12+
use std::process::Command;
13+
14+
fn assert_reverse_iterator_for_program_arguments(program_name: &str) {
15+
let args: Vec<_> = args().rev().collect();
16+
17+
assert!(args.len() == 4);
18+
assert_eq!(args[0], "c");
19+
assert_eq!(args[1], "b");
20+
assert_eq!(args[2], "a");
21+
assert_eq!(args[3], program_name);
22+
23+
println!("passed");
24+
}
25+
26+
fn main() {
27+
let mut args = args();
28+
let me = args.next().unwrap();
29+
30+
if let Some(_) = args.next() {
31+
assert_reverse_iterator_for_program_arguments(&me);
32+
return
33+
}
34+
35+
let output = Command::new(&me)
36+
.arg("a")
37+
.arg("b")
38+
.arg("c")
39+
.output()
40+
.unwrap();
41+
assert!(output.status.success());
42+
assert!(output.stderr.is_empty());
43+
assert_eq!(output.stdout, b"passed\n");
44+
}

0 commit comments

Comments
 (0)