我正在尝试编写一个连接两个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()),
}
}
}
我正在尝试清理一堆代码,我使用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"),
]);
}
}
(这是在Rust stable,版本1.23.0)
答案 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"),]
);
}
}
另见: