To avoid the XY problem, here's what I originally want to do:
I have a path: /foo/bar/bloo/baz/file.txt
and would like the most efficient way to obtain bloo/baz/file.txt
from that, essentially skipping all ancestors before a specific component (in this case "bloo"). The approach that seemed most sensible to me was to use the components function in std::path::Path.
My attempt looks like this:
let new_path = path
.components()
.skip_while(|v| if let Normal(v) = v { v.as_bytes() != b"bloo" } else { true })
.skip(1)
.as_path();
However this produces the following error:
error[E0599]: no method named `as_path` found for struct `Skip` in the current scope
--> src/main.rs:712:14
|
712 | .as_path();
| ^^^^^^^ method not found in `Skip<SkipWhile<Components<'_>, [closure@src/main.rs:710:25: 710:89]>>`
For more information about this error, try `rustc --explain E0599`.
error: could not compile `bloggers` due to previous error
Which is pretty self explanatory. I could just collect the remaining components into a string using the underlying filesystem separator, but that seems like more of a workaround than the actual solution.
In a more general sense: Can we convert a generic iterator such as Skip
or Map
back to a specialised iterator such as Components
, DirEntry
, etc. as long as we can guarantee that the Item type is the same?
edit#1:
For reference, the most efficient solution I could think of to build a new path from a string looks as follows (using the unstable intersperse
and MAIN_SEPARATOR_STR
features):
let new_path: String = path
.components()
.skip_while(|v| if let Normal(v) = v { v.as_bytes() != b"bloo" } else { true })
.skip(1)
.map(|v| v.as_os_str().to_str())
.intersperse(Some(std::path::MAIN_SEPARATOR_STR))
.collect::<Option<_>>()
.ok_or_else(|| /* error reporting of choice */)?;
The simplest way is to consume the iterator eagerly:
let mut new_path = path.components();
while let Some(v) = new_path.next() {
if let Normal(v) = v {
if v.as_bytes() == b"bloo" { // Can be just `v == "bloo"`
break;
}
}
}
let new_path = new_path.as_path();
itertools
has the dropping()
method for an eager skip()
, but doesn't have an eager equivalent to skip_while()
, unfortunately, so you have to roll your own, like I did.