如何指定 AsRef 的生命周期?

Ros*_*ght 10 rust

我正在尝试编写一个连接两个可迭代对象的函数,其中的项目可以转换为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()),
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我正在尝试清理我正在使用的一堆代码map,并chain自己强制类型(如下面的测试所示)。如果有更好的方法来做到这一点,我洗耳恭听。:)

#[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"),
        ]);
    }
}
Run Code Online (Sandbox Code Playgroud)

(这是 Rust 稳定版,版本 1.23.0)

She*_*ter 9

你不知道

AsRef是一个特质,它的定义是固定的:

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

它可以用于获取对一个事物的引用并获取具有相同生命周期的另一个引用。

您的代码将允许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());
}
Run Code Online (Sandbox Code Playgroud)

如果你随后打电话AsRef该项目,您将获得对不存在于该功能之外的某些内容的引用。但是,您试图返回该引用,这将是无效的。因此,Rust 可以防止您引入内存不安全性;万岁!

这与How to use the life 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,
{
    // ...
}
Run Code Online (Sandbox Code Playgroud)

顺便说一句,您不需要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"),]
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

也可以看看: