具有关联类型的泛型参数的生命周期问题
Lifetime issue with generic parameter with associated type
我遇到一个问题,编译器说数据被多次可变借用。我在循环中借用数据,但是借用仅在循环中局部使用,因此下一次迭代借用应该已经完成。
(对我来说)令人困惑的是,它适用于局部变量,但当借用通过特征中定义的方法发生时则无效,并且该变量作为通用类型参数的可变引用传递这与该特征有关。
以下代码是一个简化的示例,但显示了完全相同的错误:
trait GetElem<'a, T> {
type ElemRef: Deref<Target=T>;
fn get_at(&'a mut self, index: usize) -> Self::ElemRef;
}
struct Ref<'a, T>(&'a T);
impl<'a, T> Deref for Ref<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.0
}
}
impl<'a, T: 'a> GetElem<'a, T> for Vec<T> {
type ElemRef = Ref<'a, T>;
fn get_at(&'a mut self, index: usize) -> Self::ElemRef {
Ref(self.get(index).unwrap())
}
}
fn test<'a, C: 'a + GetElem<'a, i32>>(passed_data: &'a mut C) {
let mut local_data = vec![1, 2];
let x = local_data.get_at(0);
let tmp = *x;
let y = local_data.get_at(1);
let _ = tmp + *y;
let x = passed_data.get_at(0);
let tmp = *x;
let y = passed_data.get_at(1);
let _ = tmp + *y;
}
或在 playground
上查看
所以,在这个例子中,我有一个 passed_data
(在对实现 GetElem<i32>
特征的类型的可变引用后面)和一个 local_data
,这是一个Vec<i32>
.
对于两者,我尝试做同样的事情:获取第一个元素的可变引用,将值复制到临时变量,获取第二个值的可变引用,然后计算两者的和。
这对 local_data
有效,但对 passed_data
无效,并出现以下错误:
error[E0499]: cannot borrow `*passed_data` as mutable more than once at a time
.
你告诉编译器“我借用 self
,只要 'a
,我就可以保持引用存活”。并且 'a
是在结构(或特征)中定义的,这意味着您只能使用该结构一次。
你不想这样。你想说,“我借用 self
,我 return 一个与这个借用相关的类型”。如果这是一个参考,你就不会有问题。它是 fn get_at<'a>(&'a mut self) -> &'a T
(这是另一个反模式,顺便说一句,将 &mut
降级为 &
。但有时你会想要那样)。或者只是 fn get_at(&mut self) -> &T
,使用生命周期省略。如果这是一个结构,例如 Ref<'a, T>
,您也不会有问题 - 只是 return fn get_mut(&mut self) -> Ref<'_, T>
(或没有省略 fn get_mut<'a>(&'a mut self) -> Ref<'a, T>
)。但这不是其中之一. 这是关联类型。
您希望关联类型在整个生命周期内都是通用的。这称为 GAT(通用关联类型)。不幸的是,在稳定的 Rust 中是不可能的。它 可以用 nightly:
#![feature(generic_associated_types)]
use std::ops::Deref;
trait GetElem<T> {
type ElemRef<'a>: Deref<Target = T> + 'a
where
Self: 'a;
fn get_at(&mut self, index: usize) -> Self::ElemRef<'_>;
}
struct Ref<'a, T>(&'a T);
impl<T> Deref for Ref<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.0
}
}
impl<T> GetElem<T> for Vec<T> {
type ElemRef<'a>
where
T: 'a,
= Ref<'a, T>;
fn get_at(&mut self, index: usize) -> Self::ElemRef<'_> {
Ref(self.get(index).unwrap())
}
}
下面是你如何使用它:
fn test<C: GetElem<i32>>(passed_data: &mut C) {
let mut local_data = vec![1, 2];
let x = local_data.get_at(0);
let tmp = *x;
let y = local_data.get_at(1);
let _ = tmp + *y;
let x = passed_data.get_at(0);
let tmp = *x;
drop(x);
let y = passed_data.get_at(1);
let _ = tmp + *y;
}
您可能想知道为什么我需要 drop()
(x
的新范围也可以,但我更喜欢这种形式)。好吧,我没有理由不需要。 Pre-NLL (Non-Lexical Lifetimes),你也需要它作为引用——引用落在范围的末尾,只有这样借用才是免费的。但是,NLL 改进了这一点,因为如果我不再 使用 引用 - 没有理由不将其视为已删除。然而,对于结构,这不是真的:一个结构可以实现 Drop
并观察那里的引用,编译器不能更早地删除它,因为删除顺序是有保证的。
我遇到一个问题,编译器说数据被多次可变借用。我在循环中借用数据,但是借用仅在循环中局部使用,因此下一次迭代借用应该已经完成。
(对我来说)令人困惑的是,它适用于局部变量,但当借用通过特征中定义的方法发生时则无效,并且该变量作为通用类型参数的可变引用传递这与该特征有关。
以下代码是一个简化的示例,但显示了完全相同的错误:
trait GetElem<'a, T> {
type ElemRef: Deref<Target=T>;
fn get_at(&'a mut self, index: usize) -> Self::ElemRef;
}
struct Ref<'a, T>(&'a T);
impl<'a, T> Deref for Ref<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.0
}
}
impl<'a, T: 'a> GetElem<'a, T> for Vec<T> {
type ElemRef = Ref<'a, T>;
fn get_at(&'a mut self, index: usize) -> Self::ElemRef {
Ref(self.get(index).unwrap())
}
}
fn test<'a, C: 'a + GetElem<'a, i32>>(passed_data: &'a mut C) {
let mut local_data = vec![1, 2];
let x = local_data.get_at(0);
let tmp = *x;
let y = local_data.get_at(1);
let _ = tmp + *y;
let x = passed_data.get_at(0);
let tmp = *x;
let y = passed_data.get_at(1);
let _ = tmp + *y;
}
或在 playground
上查看所以,在这个例子中,我有一个 passed_data
(在对实现 GetElem<i32>
特征的类型的可变引用后面)和一个 local_data
,这是一个Vec<i32>
.
对于两者,我尝试做同样的事情:获取第一个元素的可变引用,将值复制到临时变量,获取第二个值的可变引用,然后计算两者的和。
这对 local_data
有效,但对 passed_data
无效,并出现以下错误:
error[E0499]: cannot borrow `*passed_data` as mutable more than once at a time
你告诉编译器“我借用 self
,只要 'a
,我就可以保持引用存活”。并且 'a
是在结构(或特征)中定义的,这意味着您只能使用该结构一次。
你不想这样。你想说,“我借用 self
,我 return 一个与这个借用相关的类型”。如果这是一个参考,你就不会有问题。它是 fn get_at<'a>(&'a mut self) -> &'a T
(这是另一个反模式,顺便说一句,将 &mut
降级为 &
。但有时你会想要那样)。或者只是 fn get_at(&mut self) -> &T
,使用生命周期省略。如果这是一个结构,例如 Ref<'a, T>
,您也不会有问题 - 只是 return fn get_mut(&mut self) -> Ref<'_, T>
(或没有省略 fn get_mut<'a>(&'a mut self) -> Ref<'a, T>
)。但这不是其中之一. 这是关联类型。
您希望关联类型在整个生命周期内都是通用的。这称为 GAT(通用关联类型)。不幸的是,在稳定的 Rust 中是不可能的。它 可以用 nightly:
#![feature(generic_associated_types)]
use std::ops::Deref;
trait GetElem<T> {
type ElemRef<'a>: Deref<Target = T> + 'a
where
Self: 'a;
fn get_at(&mut self, index: usize) -> Self::ElemRef<'_>;
}
struct Ref<'a, T>(&'a T);
impl<T> Deref for Ref<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.0
}
}
impl<T> GetElem<T> for Vec<T> {
type ElemRef<'a>
where
T: 'a,
= Ref<'a, T>;
fn get_at(&mut self, index: usize) -> Self::ElemRef<'_> {
Ref(self.get(index).unwrap())
}
}
下面是你如何使用它:
fn test<C: GetElem<i32>>(passed_data: &mut C) {
let mut local_data = vec![1, 2];
let x = local_data.get_at(0);
let tmp = *x;
let y = local_data.get_at(1);
let _ = tmp + *y;
let x = passed_data.get_at(0);
let tmp = *x;
drop(x);
let y = passed_data.get_at(1);
let _ = tmp + *y;
}
您可能想知道为什么我需要 drop()
(x
的新范围也可以,但我更喜欢这种形式)。好吧,我没有理由不需要。 Pre-NLL (Non-Lexical Lifetimes),你也需要它作为引用——引用落在范围的末尾,只有这样借用才是免费的。但是,NLL 改进了这一点,因为如果我不再 使用 引用 - 没有理由不将其视为已删除。然而,对于结构,这不是真的:一个结构可以实现 Drop
并观察那里的引用,编译器不能更早地删除它,因为删除顺序是有保证的。