函数vs结构的类型参数(生命周期问题) [英] type parameter for function vs struct (lifetime issue)
本文介绍了函数vs结构的类型参数(生命周期问题)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
考虑以下测试案例:
#![allow(unstable)]
trait Choose<'o> {
fn choose(a: &'o u64, b: &'o u32) -> Self;
}
impl<'o> Choose<'o> for &'o u64 {
fn choose(a: &'o u64, _b: &'o u32) -> &'o u64 { a }
}
impl<'o> Choose<'o> for &'o u32 {
fn choose(_a: &'o u64, b: &'o u32) -> &'o u32 { b }
} // '
struct Handler {
a: u64,
b: u32,
}
impl Handler {
fn new() -> Handler {
Handler { a: 14, b: 15 }
}
fn find<'a, V, W>(&'a mut self, value: W) -> Option<V> where V: Choose<'a>, W: PartialEq<V> { // '
let v = Choose::choose(&self.a, &self.b);
if value == v {
Some(v)
} else {
None
}
}
}
fn main() {
let mut h = Handler::new();
{
let v_a = h.find::<&u64, &u64>(&14u64);
println!("v_a = {:?}", v_a);
}
{
let v_b = h.find::<&u64, &u64>(&15u64);
println!("v_b = {:?}", v_b);
}
}
假设我在 Handler :: find 中有一些变化的状态,所以我需要& mut self .但是指向 Handler 内部的 v_a 和 v_b 变量都位于它们自己的块中,因此这里没有借用问题.在这种情况下,直接为 find 方法指定类型参数 V ,并且所有内容都会按预期编译.
但是随后我将参数 V 移到了 Handler 类型签名中,并且它停止编译,并出现无法多次借用h
作为可变变量"错误:
#![allow(unstable)]
trait Choose<'o> {
fn choose(a: &'o u64, b: &'o u32) -> Self;
}
impl<'o> Choose<'o> for &'o u64 {
fn choose(a: &'o u64, _b: &'o u32) -> &'o u64 { a }
}
impl<'o> Choose<'o> for &'o u32 {
fn choose(_a: &'o u64, b: &'o u32) -> &'o u32 { b }
} // '
struct Handler<V> {
a: u64,
b: u32,
}
impl<V> Handler<V> {
fn new() -> Handler<V> {
Handler { a: 14, b: 15 }
}
fn find<'a, W>(&'a mut self, value: W) -> Option<V> where V: Choose<'a>, W: PartialEq<V> { // '
let v = Choose::choose(&self.a, &self.b);
if value == v {
Some(v)
} else {
None
}
}
}
fn main() {
let mut h = Handler::<&u64>::new();
{
let v_a = h.find(&14u64);
println!("v_a = {:?}", v_a);
}
{
let v_b = h.find(&15u64);
println!("v_b = {:?}", v_b);
}
}
我真的不明白其中的区别.为什么变量 v_a 死后不释放可变借位?
解决方案
我认为这里发生的是:
在main
中,当您执行let mut h = Handler::<&u64>::new();
时,您的Handler现在已绑定到对该u64
的引用的生存期.
因此,即使v_a
在以下代码块中死亡,V的生存期也必须是h
的生存期.
顺便说一句,问题不仅仅在于您已经编写的代码,而是您或其他人仍然可以编写的代码. 给定您对Handler的定义,使其具有不受约束的V,我可以继续进行以下操作:
// in the meanwhile, in another crate...
// I create another trait
trait MyTrait {
fn foo(&self) -> &u64;
}
// and implement it for Handler<&u64>
impl<'a> MyTrait for Handler<&'a u64> {
fn foo(&self) -> &u64 { &self.a }
}
然后这将是合法的:
let h = Handler::<&u64>::new();
println!("{}", h.foo()); // prints 14
所以,每当我像您一样做let h = Handler::<&u64>::new();
时,唯一安全的选择是让& 64的寿命至少与h一样长.
如果可以将u64
用作V
,而不是&u64
,则可以.这样的事情几乎不会改变您的程序(请注意,我仍在使用引用,而不是按值传递),但是允许您为u32/64而不是& u32/64设置Handler的参数:
trait Choose<'o> {
fn choose(a: &'o u64, b: &'o u32) -> &'o Self;
}
impl<'o> Choose<'o> for u64 {
fn choose(a: &'o u64, _b: &'o u32) -> &'o u64 { a }
}
impl<'o> Choose<'o> for u32 {
fn choose(_a: &'o u64, b: &'o u32) -> &'o u32 { b }
}
struct Handler<V> {
a: u64,
b: u32,
}
impl<V> Handler<V> {
fn new() -> Handler<V> {
Handler { a: 14, b: 15 }
}
fn find<'a, W>(&'a mut self, value: W) -> Option<&'a V> where V: Choose<'a>, W: PartialEq<&'a V> {
let v = Choose::choose(&self.a, &self.b);
if value == v {
Some(v)
} else {
None
}
}
}
fn main() {
let mut h = Handler::<u64>::new();
{
let v_a = h.find(&14u64);
println!("v_a = {:?}", v_a);
}
{
let v_b = h.find(&15u64);
println!("v_b = {:?}", v_b);
}
}
查看全文