闭包返回引用致泛型函数编译失败,生命周期问题求解
解决泛型函数中使用itertools unique_by接收返回引用的闭包的生命周期问题
我想实现一个泛型函数,用itertools的unique_by方法过滤向量中的非唯一值,闭包返回用于比较的数据的引用。直接在函数内写闭包能正常编译,但把闭包作为参数传入泛型函数时出现生命周期错误。
代码示例
use itertools::Itertools; use std::hash::Hash; #[derive(Clone)] struct Person { uints: Vec<usize>, } #[derive(Clone)] struct Dog { ints: Vec<i8>, } fn filter_duplicates(population: &[Person]) -> Vec<Person> { population .iter() .unique_by(|x| &x.uints) .cloned() .collect::<Vec<_>>() } fn filter_duplicates_generic<T: Clone, V: Eq + Hash, F: Fn(&&T) -> V>( population: &[T], unique_representation: F, ) -> Vec<T> { population .iter() .unique_by(unique_representation) .cloned() .collect::<Vec<_>>() } fn main() { let people = vec![ Person { uints: vec![0, 1, 2] }, Person { uints: vec![0, 1, 2] }, Person { uints: vec![3, 4, 5] }, ]; let dogs = vec![Dog { ints: vec![0] }]; // 正常编译 filter_duplicates(&people); // 编译失败 filter_duplicates_generic(&people, |x| &x.uints); filter_duplicates_generic(&dogs, |x| &x.ints); }
编译错误信息
lifetime may not live long enough returning this value requires that `'1` must outlive `'2` main.rs(45, 64): has type `&&'1 Person` main.rs(45, 65): return type of closure is `&'2 std::vec::Vec<usize>`
问题原因
你的泛型函数filter_duplicates_generic的闭包类型约束F: Fn(&&T) -> V没有明确V(当它是引用时)的生命周期与输入参数的关系。当闭包返回&x.uints这类引用时,该引用的生命周期依赖于输入&T的生命周期,但编译器无法自动推断出这两个生命周期必须绑定,因此报错。
而itertools的unique_by方法之所以能正常工作,是因为它的签名利用了迭代器的生命周期:
unique_by的闭包接收的是迭代器的元素(即&'a T,其中'a是输入切片的生命周期)- 闭包返回的
K可以是与'a绑定的引用(比如&'a V),编译器能自动推断出K的生命周期不能超过迭代器元素的生命周期,因此不会出现生命周期不匹配的问题。
解决方案
有两种方式修正你的泛型函数:
方式1:显式指定生命周期绑定
给泛型参数添加生命周期,让闭包返回的引用与输入切片的生命周期一致:
fn filter_duplicates_generic<'a, T: Clone, V: Eq + Hash, F: Fn(&'a T) -> &'a V>( population: &'a [T], unique_representation: F, ) -> Vec<T> { population .iter() .unique_by(|x| unique_representation(x)) .cloned() .collect::<Vec<_>>() }
方式2:使用高阶生命周期(HRTB)
这种方式更通用,允许闭包接受任意生命周期的&T并返回对应生命周期的&V:
fn filter_duplicates_generic<T: Clone, V: Eq + Hash, F>( population: &[T], unique_representation: F, ) -> Vec<T> where for<'a> F: Fn(&'a T) -> &'a V, { population .iter() .unique_by(|x| unique_representation(x)) .cloned() .collect::<Vec<_>>() }
为什么直接写闭包时没问题?
在filter_duplicates函数内部,编译器能在同一个作用域中推断出闭包返回的引用的生命周期与输入切片population的生命周期一致,不需要显式声明。但当闭包作为参数传入泛型函数时,必须通过生命周期约束明确这种依赖关系,否则编译器无法确定引用的有效性。
内容的提问来源于stack exchange,提问作者Carpet4
相关产品推荐
相关产品推荐

