能否为AsRef trait指定'static生命周期?附解决方案探讨
能不能给AsRef trait指定特定的生命周期(比如'static)?实际尝试后发现不可行,下面详细说明原因及可行方案。
问题场景
定义一个编译期值已知的Car单元枚举,希望实现AsRef<str>时返回&'static str:
enum Car { Ferrari, Fiat, } impl AsRef<str> for Car { fn as_ref(&self) -> &'static str { match self { Self::Ferrari => "ferrari", Self::Fiat => "fiat", } } }
虽然实现里把as_ref的返回签名改成了&'static str,但通过trait调用时,编译器仍会遵循AsRef自身的生命周期规则,无法获得'static生命周期。比如执行以下代码会报错:
let cars: Vec<Car> = vec![Car::Ferrari, Car::Fiat]; let names: Vec<&'static str> = cars.into_iter().map(|car| car.as_ref()).collect();
错误信息:
cannot return value referencing function parameter
carreturns a value referencing data owned by the current function
原因解析
AsRef trait的核心定义是固定的,其as_ref方法的返回引用生命周期与self的生命周期绑定:
pub trait AsRef<T: ?Sized> { fn as_ref(&self) -> &T; }
根据Rust的生命周期省略规则,这里的&T等价于&'a T,其中'a是&self的生命周期。也就是说,无论实现时写什么生命周期,通过trait调用时,编译器只会认为返回引用的生命周期和self一致。
在报错的代码中,into_iter()取出的car是闭包内的临时值,生命周期仅限于闭包执行期间,编译器会默认as_ref()返回的引用和car同生命周期,自然无法转换成'static,从而触发错误。
可行解决方案
自定义一个明确返回'static引用的trait:
pub trait AsStaticRef<T> where T: ?Sized, { fn as_static_ref(&self) -> &'static T; } impl AsStaticRef<str> for Car { fn as_static_ref(&self) -> &'static str { match self { Self::Ferrari => "ferrari", Self::Fiat => "fiat", } } }
这个trait的方法签名明确指定返回&'static T,编译器能正确识别返回值的生命周期,调用时不会再和self的生命周期绑定,完美解决问题。
内容的提问来源于stack exchange,提问作者Max

