关联类型中受限生命周期参数变为不受限的问题
问题描述
我编写以下代码时可正常编译:
impl<'a, T1, T2> Mul<TopOf<T1>> for LeftOf<T2> where T1: Into<ObjRef<'a>>, T2: Into<ObjRef<'a>>, { type Output = f32; fn mul(self, rhs: TopOf<T1>) -> Self::Output { todo!() } }
但修改关联类型Output为如下形式时:
impl<'a, T1, T2> Mul<TopOf<T1>> for LeftOf<T2> where T1: Into<ObjRef<'a>>, T2: Into<ObjRef<'a>>, { type Output = Location<ObjRef<'a>>; fn mul(self, rhs: TopOf<T1>) -> Self::Output { todo!() } }
会收到错误提示,称生命周期参数'a不受限。约束条件完全一致,仅Output类型使用方式不同,我无法理解错误合理性。这是编译器提示问题还是我的理解偏差?我遗漏了什么?
注:有相关技术回答适用于无需生命周期的场景,但我此处必须使用该生命周期。
问题原因
这是Rust生命周期约束检查规则导致的,核心逻辑是:当关联类型Output引用了生命周期参数'a时,编译器要求这个生命周期必须被impl的**输入类型(目标类型LeftOf<T2>或操作数类型TopOf<T1>)**所约束,不能是一个游离的、不受限的参数。
第一个可编译的代码中,Output是无生命周期依赖的f32,所以编译器不关心'a是否被输入类型绑定——即便'a看起来没有直接和T1/T2的生命周期挂钩,只要where子句的转换约束满足,就能通过编译。
但当Output变为Location<ObjRef<'a>>后,'a成为了关联类型的一部分,此时编译器需要明确'a的来源:它必须由T1/T2本身的生命周期决定,而不是任意指定的生命周期。你的where子句只要求T1/T2能转换为ObjRef<'a>,但没有建立'a与T1/T2的绑定关系,导致编译器判定'a不受限。
解决方法
你需要让生命周期'a与T1或T2的生命周期绑定,常见的实现方式有三种:
方法1:为T1/T2添加生命周期约束
通过T1: 'a和T2: 'a,让'a被T1/T2的生命周期所约束(表示T1/T2的所有生命周期参数都不短于'a):
impl<'a, T1: 'a, T2: 'a> Mul<TopOf<T1>> for LeftOf<T2> where T1: Into<ObjRef<'a>>, T2: Into<ObjRef<'a>>, { type Output = Location<ObjRef<'a>>; fn mul(self, rhs: TopOf<T1>) -> Self::Output { todo!() } }
方法2:使用高阶生命周期(HRTB)
如果T1/T2可以适配任意生命周期的ObjRef,可以用for<'a>语法让impl适用于所有可能的生命周期,避免'a不受限的问题:
impl<T1, T2> Mul<TopOf<T1>> for LeftOf<T2> where for<'a> T1: Into<ObjRef<'a>>, for<'a> T2: Into<ObjRef<'a>>, { type Output = Location<ObjRef<'static>>; // 需根据实际需求调整生命周期 fn mul(self, rhs: TopOf<T1>) -> Self::Output { todo!() } }
方法3:关联目标类型的生命周期
如果LeftOf<T2>本身带有生命周期参数,直接复用该参数作为'a,自然满足约束:
// 假设LeftOf的定义为 struct LeftOf<'b, T>(T); impl<'a, T1, T2> Mul<TopOf<T1>> for LeftOf<'a, T2> where T1: Into<ObjRef<'a>>, T2: Into<ObjRef<'a>>, { type Output = Location<ObjRef<'a>>; fn mul(self, rhs: TopOf<T1>) -> Self::Output { todo!() } }
内容的提问来源于stack exchange,提问作者Olivetree

