我正在try 使用一个特征Context,它的不同实现允许Cell以不同的方式访问它的所有者World,如以下人工设计的示例代码(编译;playground here)所示:

struct Cell {    
    value: i32,    
} 
    
impl Cell {    
    fn evolve<'a>(&self, ctx: impl Context<'a>) -> Cell {    
        Cell {    
            value: self.value + ctx.get_rate()    
        }    
    }    
}    
    
struct World {    
    cell: Cell,    
    magic: i32,    
}    
    
impl World {    
    fn evolve_cell<'a, C: Context<'a>>(&'a self) -> Cell {    
        let ctx = C::new(self);        
        self.cell.evolve(ctx)        
    }    
        
    fn update(&mut self) {    
        self.cell = self.evolve_cell::<MagicContext>();    
    }    
}        
         
trait Context<'a> {    
    fn new(world: &'a World) -> Self;    
    fn get_rate(&self) -> i32;    
}    
     
struct MagicContext<'a> {    
    world: &'a World,        
}                            
                             
impl <'a> Context<'a> for MagicContext<'a> {                     
    fn new(world: &'a World) -> Self {    
        MagicContext {    
            world    
        }    
    }        
             
    fn get_rate(&self) -> i32 {    
        self.world.magic    
    }    
}        
         
fn main() {    
    let mut world = World {    
        cell: Cell { value: 2 },    
        magic: 3,    
    };    
          
    world.update();    
    println!("{}", world.cell.value);    
}

我希望能够在main中 Select 调用点的实现,而不是将MagicContext指定为World::update中的Context实现--如下所示:

impl World {    
    // ...

    fn update<C: Context>(&mut self) {    
        self.cell = self.evolve_cell::<C>();    
    }
}

// ...

fn main() {
    // ...
    world.update::<MagicContext>();
    // ...
}

但是Context需要一个生命周期参数.我不能引入像fn update<'a, C: Context<'a>>(&mut self)这样的生命周期参数,因为上下文的生命周期将阻止我Mutations self:

error[E0506]: cannot assign to `self.cell` because it is borrowed
  --> lifetime.rs:25:9
   |
24 |     fn update<'a, C: Context<'a>>(&'a mut self) {
   |               -- lifetime `'a` defined here
25 |         self.cell = self.evolve_cell::<C>();
   |         ^^^^^^^^^^^^-----------------------
   |         |           |
   |         |           borrow of `self.cell` occurs here
   |         |           argument requires that `*self` is borrowed for `'a`
   |         assignment to borrowed `self.cell` occurs here

一些看似相似的问题让我看了一下更高等级的特征界限,大约是fn update<C: for <'a> Context<'a>>(&mut self)分,但编译器告诉我:

error: implementation of `Context` is not general enough
  --> lifetime.rs:56:11
   |
56 |     world.update::<MagicContext>();
   |           ^^^^^^ implementation of `Context` is not general enough
   |
   = note: `Context<'0>` would have to be implemented for the type `MagicContext<'_>`, for any lifetime `'0`...
   = note: ...but `Context<'1>` is actually implemented for the type `MagicContext<'1>`, for some specific lifetime `'1`

我希望能够完全摆脱Context的生命周期参数,但如果没有它,我不知道如何表达new函数.

我如何才能做到这一点(或者以一种更惯用的方式实现某种类似功能)?

推荐答案

可以使用GATs(playground)解决此问题:

#![feature(generic_associated_types)]

struct Cell {
    value: i32,
}

impl Cell {
    fn evolve(&self, ctx: impl Context) -> Cell {
        Cell {
            value: self.value + ctx.get_rate(),
        }
    }
}

struct World {
    cell: Cell,
    magic: i32,
}

impl World {
    fn evolve_cell<C: Context>(&self) -> Cell {
        let ctx = C::new(self);
        self.cell.evolve(ctx)
    }

    fn update<C: Context>(&mut self) {
        self.cell = self.evolve_cell::<C>();
    }
}

trait Context {
    type Instance<'a>: Context;
    fn new<'a>(world: &'a World) -> Self::Instance<'a>;
    fn get_rate(&self) -> i32;
}

struct MagicContext<'a> {
    world: &'a World,
}

impl<'a> Context for MagicContext<'a> {
    type Instance<'b> = MagicContext<'b>;
    
    fn new<'b>(world: &'b World) -> Self::Instance<'b> {
        MagicContext { world }
    }

    fn get_rate(&self) -> i32 {
        self.world.magic
    }
}

不幸的是,盖茨是不稳定的.不过,生命期Gats有一个解决办法:使用关联的类型创建另一个特征:

trait ContextInstance<'a> {
    type Instance: Context;
}
trait Context: for<'a> ContextInstance<'a> {
    fn new<'a>(world: &'a World) -> <Self as ContextInstance<'a>>::Instance;
    fn get_rate(&self) -> i32;
}

impl<'a, 'b> ContextInstance<'b> for MagicContext<'a> {
    type Instance = MagicContext<'b>;
}
impl<'a> Context for MagicContext<'a> {
    fn new<'b>(world: &'b World) -> <Self as ContextInstance<'b>>::Instance {
        MagicContext { world }
    }

    fn get_rate(&self) -> i32 {
        self.world.magic
    }
}

Playground.

Rust相关问答推荐

当一个箱子有自己的依赖关系时,两个人如何克服S每箱1库+n箱的限制?

限制未使用的泛型导致编译错误

关于如何初始化弱 struct 字段的语法问题

在UdpSocket上使用sendto时的隐式套接字绑定

如何正确地将App handler传递给Tauri中的其他模块?

如何定义实现同名但返回类型不同的 struct 的函数

Tokio_Postgres行上未显示退回特性的生存期,且生命周期 不够长

为什么我们需要std::thread::scope,如果我们可以使用thread.join()在函数的生命周期内删除引用?

为什么铁 rust S的默认排序功能比我对小数组的 Select 排序稍微慢一些?

Trait bound i8:来自u8的不满意

在复制类型中使用std::ptr::WRITE_VILAR进行内部可变性的安全性(即没有UnSafeCell)

Rust ndarray:如何从索引中 Select 数组的行

实现 Deref 的 struct 可以返回对外部数据的引用吗?

Rust Option 的空显式泛型参数

火箭整流罩、tokio-scheduler 和 cron 的生命周期问题

有没有办法隐藏类型定义?

BigUint 二进制补码

如何制作具有关联类型的特征的类型擦除版本?

当引用不再被borrow 时,Rust 不会得到它

如何在 Rust 中使用特征标志来捕获多行代码?