如何指定AsRef的生命周期?

时间:2018-02-11 17:18:40

标签: rust

我正在尝试编写一个连接两个iterables的函数,这些函数的项可以转换为OsStr引用,并且在尝试指定引用的生命周期时遇到了很大的困难。

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()),
        }
    }
}

我正在尝试清理一堆代码,我使用mapchain来强制自己强制类型(如下面的测试)。如果有更好的方法来做到这一点,我会全力以赴。 :)

#[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"),
        ]);
    }
}

(这是在Rust stable,版本1.23.0)

1 个答案:

答案 0 :(得分:2)

你没有

AsRef是一个特征,其定义是固定的:

pub trait AsRef<T>
where
    T: ?Sized, 
{
    fn as_ref(&self) -> &T;
}

它只能 用于引用一件事并获得具有相同生命周期的另一个引用。

您的代码将允许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());
}

如果您随后在该项目上调用了AsRef,那么您将引用一些超出该功能的内容。但是,您试图返回该引用,这将是无效的。因此,Rust阻止你引入记忆不安全;万岁!

这与How to use the lifetime on AsRef

完全相同

好消息是你可以表达你想要的东西,你只需要声明你的迭代器返回引用

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,
{
    // ...
}

顺便说一下,你的结构上不需要PhantomData或生命周期:

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"),]
        );
    }
}

另见: