I'm trying to write a function that joins two iterables whose items can be converted into OsStr
references and have had a tremendous amount of difficulty trying to specify the reference's lifetime.
use std::convert::AsRef;
use std::ffi::OsStr;
use std::marker::PhantomData;
#[derive(Clone, Debug)]
#[must_use = "join_args is lazy and does nothing unless consumed"]
pub struct JoinArgs<'a, A: 'a, B: 'a> {
a: A,
b: B,
state: JoinState,
phantomA: PhantomData<&'a A>,
phantomB: PhantomData<&'a B>,
}
#[derive(Clone, Debug)]
enum JoinState {
Both,
Front,
Back,
}
/// Chains two iterable argument lists.
pub fn join_args<'a, I1, S1, I2, S2>(iter1: I1, iter2: I2) -> JoinArgs<'a, I1::IntoIter, I2::IntoIter>
where
I1: IntoIterator<Item = S1>,
S1: AsRef<OsStr> + 'a,
I2: IntoIterator<Item = S2>,
S2: AsRef<OsStr> + 'a
{
let a = iter1.into_iter();
let b = iter2.into_iter();
JoinArgs{a, b, state: JoinState::Both, phantomA: PhantomData, phantomB: PhantomData}
}
impl<'a, A, SA, B, SB> Iterator for JoinArgs<'a, A, B>
where
A: Iterator<Item = SA>,
SA: AsRef<OsStr> + 'a,
B: Iterator<Item = SB>,
SB: AsRef<OsStr> + 'a
{
type Item = &'a OsStr;
fn next(&mut self) -> Option<Self::Item> {
// All throughout here, I'm getting E0597 errors.
match self.state {
JoinState::Both => match self.a.next() {
Some(x) => Some(x.as_ref()),
None => {
self.state = JoinState::Back;
self.b.next().map(|x| x.as_ref())
}
},
JoinState::Front => self.a.next().map(|x| x.as_ref()),
JoinState::Back => self.b.next().map(|x| x.as_ref()),
}
}
}
I'm trying to clean up a bunch of code I have where I'm using map
and chain
to coerce the types myself (like in the test below). If there's a better way to do this, I'm all ears. :)
#[cfg(test)]
mod tests {
use super::*;
use std::ffi::OsString;
#[test]
fn test_join_args() {
let a = &[OsStr::new("abc"), OsStr::new("def")];
let b = vec![OsString::from("ghi")];
let result: Vec<&OsStr> = join_args(a, &b).collect();
assert_eq!(result, [
OsStr::new("abc"),
OsStr::new("def"),
OsStr::new("ghi"),
]);
}
}
(This is on Rust stable, version 1.23.0)
You don't.
AsRef
is a trait and its definition is fixed:
pub trait AsRef<T>
where
T: ?Sized,
{
fn as_ref(&self) -> &T;
}
It can only be used to take a reference to one thing and get another reference with the same lifetime.
Your code would allow an Iterator<Item = OsString>
:
use std::ffi::{OsStr, OsString};
fn proof<'a, I>(_: I)
where
I: Iterator,
I::Item: AsRef<OsStr> + 'a,
{}
fn main() {
proof(vec![OsString::new()].into_iter());
}
If you then called AsRef
on the item, you'd have a reference to something that doesn't live beyond the function. However, you are attempting to return that reference, which would be invalid. Thus, Rust has prevented you from introducing memory unsafety; hooray!
This is the exact same problem as How to use the lifetime on AsRef
The good news is that you can express what you want, you just need to state that your iterator returns references:
impl<'a, A, B, S1, S2> Iterator for JoinArgs<'a, A, B>
where
A: Iterator<Item = &'a S1>,
S1: AsRef<OsStr> + 'a,
B: Iterator<Item = &'a S2>,
S2: AsRef<OsStr> + 'a,
{
// ...
}
As an aside, you don't need to have the PhantomData
or the lifetime on your struct:
use std::convert::AsRef;
use std::ffi::OsStr;
#[derive(Clone, Debug)]
#[must_use = "join_args is lazy and does nothing unless consumed"]
pub struct JoinArgs<A, B> {
a: A,
b: B,
state: JoinState,
}
#[derive(Clone, Debug)]
enum JoinState {
Both,
Front,
Back,
}
/// Chains two iterable argument lists.
pub fn join_args<I1, I2>(iter1: I1, iter2: I2) -> JoinArgs<I1::IntoIter, I2::IntoIter>
where
I1: IntoIterator,
I2: IntoIterator,
{
JoinArgs {
a: iter1.into_iter(),
b: iter2.into_iter(),
state: JoinState::Both,
}
}
impl<'a, A, B, S1, S2> Iterator for JoinArgs<A, B>
where
A: Iterator<Item = &'a S1>,
S1: AsRef<OsStr> + 'a,
B: Iterator<Item = &'a S2>,
S2: AsRef<OsStr> + 'a,
{
type Item = &'a OsStr;
fn next(&mut self) -> Option<Self::Item> {
match self.state {
JoinState::Both => match self.a.next() {
Some(x) => Some(x.as_ref()),
None => {
self.state = JoinState::Back;
self.b.next().map(AsRef::as_ref)
}
},
JoinState::Front => self.a.next().map(AsRef::as_ref),
JoinState::Back => self.b.next().map(AsRef::as_ref),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::ffi::OsString;
#[test]
fn test_join_args() {
let a = &[OsStr::new("abc"), OsStr::new("def")];
let b = vec![OsString::from("ghi")];
let result: Vec<&OsStr> = join_args(a, &b).collect();
assert_eq!(
result,
[OsStr::new("abc"), OsStr::new("def"), OsStr::new("ghi"),]
);
}
}
See also:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With