Rust实现Iterator Pattern时遭遇生命周期编译错误求助
解决Rust中迭代器装饰器的生命周期编译错误
问题背景
我在Rust中实现迭代器模式,包含两个迭代器:CommitIterator(对应Git迭代器)和ConventionalCommitIterator,其中ConventionalCommitIterator作为CommitIterator的装饰器存在。使用Box和trait模拟接口时,编译出现以下错误:
error: lifetime may not live long enough --> src/main.rs:99:13 | 92 | impl<'repo> ConventionalCommitsRepository<'repo> for ConventionalCommitsRepositoryImpl<'repo> { | ----- lifetime `'repo` defined here ... 99 | git_iterator, | ^^^^^^^^^^^^ coercion requires that `'repo` must outlive `'static`
原代码
use std::error::Error; #[derive(Clone)] pub struct Commit { pub value: i32, } pub trait GitRepository<'repo> { fn get_desc_iterator(&'repo self) -> Result<Box<dyn CommitIterator + 'repo>, Box<dyn Error>>; } pub struct GitRepositoryImpl { commits: Vec<Commit>, } impl<'repo> GitRepository<'repo> for GitRepositoryImpl { fn get_desc_iterator(&'repo self) -> Result<Box<dyn CommitIterator + 'repo>, Box<dyn Error>> { return Ok(Box::new(CommitIteratorImpl { commits: self.commits.clone(), index: 0, })); } } pub trait CommitIterator { fn next(&mut self) -> Option<Commit>; } pub struct CommitIteratorImpl { commits: Vec<Commit>, index: usize, } impl CommitIterator for CommitIteratorImpl { fn next(&mut self) -> Option<Commit> { if self.index >= self.commits.len() { return None; } let commit = self.commits[self.index].clone(); self.index += 1; return Some(commit); } } pub trait ConventionalCommitsRepository<'repo> { fn get_desc_iterator( &'repo self, ) -> Result<Box<dyn ConventionalCommitIterator + 'repo>, Box<dyn Error>>; } pub struct ConventionalCommit { pub value: i32, } pub trait ConventionalCommitIterator { fn has_next(&self) -> bool; fn next(&mut self) -> Option<ConventionalCommit>; } pub struct ConventionalCommitIteratorImpl { git_iterator: Box<dyn CommitIterator>, } impl ConventionalCommitIterator for ConventionalCommitIteratorImpl { fn has_next(&self) -> bool { false } fn next(&mut self) -> Option<ConventionalCommit> { let commit = self.git_iterator.next(); if commit.is_none() { return None; } let commit = commit.unwrap(); Some(ConventionalCommit { value: commit.value }) } } pub struct ConventionalCommitsRepositoryImpl<'repo> { git_repository: Box<dyn GitRepository<'repo>>, } impl<'repo> ConventionalCommitsRepositoryImpl<'repo> { pub fn new(git_repository: Box<dyn GitRepository<'repo>>) -> Self { Self { git_repository } } } impl<'repo> ConventionalCommitsRepository<'repo> for ConventionalCommitsRepositoryImpl<'repo> { fn get_desc_iterator( &'repo self, ) -> Result<Box<dyn ConventionalCommitIterator + 'repo>, Box<dyn Error>> { let git_iterator = self.git_repository.get_desc_iterator()?; Ok(Box::new(ConventionalCommitIteratorImpl { git_iterator, })) } } fn main() { let mut git = GitRepositoryImpl { commits: vec![Commit { value: 1 }, Commit { value: 2 }], }; let mut iterator = git.get_desc_iterator().unwrap(); let commit = iterator.next().unwrap(); println!("{}", commit.value); let commit = iterator.next().unwrap(); println!("{}", commit.value); }
错误原因
ConventionalCommitIteratorImpl中定义的git_iterator字段类型为Box<dyn CommitIterator>,在Rust中,未指定生命周期的trait对象默认使用'static生命周期。但我们从git_repository.get_desc_iterator()获取的迭代器是带有'repo生命周期的Box<dyn CommitIterator + 'repo>,将其赋值给要求'static的字段时,编译器会强制要求'repo必须存活到'static,这显然无法满足,因此报错。
解决方案
给ConventionalCommitIteratorImpl添加生命周期参数,让其持有的迭代器生命周期与传入的迭代器保持一致:
- 修改
ConventionalCommitIteratorImpl的定义,添加生命周期参数:
pub struct ConventionalCommitIteratorImpl<'a> { git_iterator: Box<dyn CommitIterator + 'a>, }
- 实现
ConventionalCommitIterator时带上对应的生命周期:
impl<'a> ConventionalCommitIterator for ConventionalCommitIteratorImpl<'a> { fn has_next(&self) -> bool { false } fn next(&mut self) -> Option<ConventionalCommit> { self.git_iterator.next().map(|commit| ConventionalCommit { value: commit.value }) } }
- 在
ConventionalCommitsRepositoryImpl的get_desc_iterator方法中,返回的Box<dyn ConventionalCommitIterator + 'repo>会自动匹配ConventionalCommitIteratorImpl<'repo>的生命周期,此时类型约束满足,编译通过。
修改后的完整代码
use std::error::Error; #[derive(Clone)] pub struct Commit { pub value: i32, } pub trait GitRepository<'repo> { fn get_desc_iterator(&'repo self) -> Result<Box<dyn CommitIterator + 'repo>, Box<dyn Error>>; } pub struct GitRepositoryImpl { commits: Vec<Commit>, } impl<'repo> GitRepository<'repo> for GitRepositoryImpl { fn get_desc_iterator(&'repo self) -> Result<Box<dyn CommitIterator + 'repo>, Box<dyn Error>> { Ok(Box::new(CommitIteratorImpl { commits: self.commits.clone(), index: 0, })) } } pub trait CommitIterator { fn next(&mut self) -> Option<Commit>; } pub struct CommitIteratorImpl { commits: Vec<Commit>, index: usize, } impl CommitIterator for CommitIteratorImpl { fn next(&mut self) -> Option<Commit> { if self.index >= self.commits.len() { return None; } let commit = self.commits[self.index].clone(); self.index += 1; Some(commit) } } pub trait ConventionalCommitsRepository<'repo> { fn get_desc_iterator( &'repo self, ) -> Result<Box<dyn ConventionalCommitIterator + 'repo>, Box<dyn Error>>; } pub struct ConventionalCommit { pub value: i32, } pub trait ConventionalCommitIterator { fn has_next(&self) -> bool; fn next(&mut self) -> Option<ConventionalCommit>; } pub struct ConventionalCommitIteratorImpl<'a> { git_iterator: Box<dyn CommitIterator + 'a>, } impl<'a> ConventionalCommitIterator for ConventionalCommitIteratorImpl<'a> { fn has_next(&self) -> bool { false } fn next(&mut self) -> Option<ConventionalCommit> { self.git_iterator.next().map(|commit| ConventionalCommit { value: commit.value }) } } pub struct ConventionalCommitsRepositoryImpl<'repo> { git_repository: Box<dyn GitRepository<'repo>>, } impl<'repo> ConventionalCommitsRepositoryImpl<'repo> { pub fn new(git_repository: Box<dyn GitRepository<'repo>>) -> Self { Self { git_repository } } } impl<'repo> ConventionalCommitsRepository<'repo> for ConventionalCommitsRepositoryImpl<'repo> { fn get_desc_iterator( &'repo self, ) -> Result<Box<dyn ConventionalCommitIterator + 'repo>, Box<dyn Error>> { let git_iterator = self.git_repository.get_desc_iterator()?; Ok(Box::new(ConventionalCommitIteratorImpl { git_iterator, })) } } fn main() { let mut git = GitRepositoryImpl { commits: vec![Commit { value: 1 }, Commit { value: 2 }], }; let mut iterator = git.get_desc_iterator().unwrap(); let commit = iterator.next().unwrap(); println!("{}", commit.value); let commit = iterator.next().unwrap(); println!("{}", commit.value); }
内容的提问来源于stack exchange,提问作者Azat Kaiumov
相关产品推荐
相关产品推荐

