use std::marker::PhantomData;
pub trait StartableApplication<'a, 'b>
where 'b: 'a
{
type Application: UpdateWithContext<Context<'a, 'b>>;
fn start(self, engine: &mut Engine<'b>) -> Self::Application;
}
pub trait UpdateWithContext<Context> {
fn update(&mut self, context: Context);
}
pub struct Context<'a, 'b>
where 'b: 'a
{
pub engine: &'a mut Engine<'b>,
}
pub struct Engine<'a> {
phantom_data: PhantomData<&'a mut i32>,
}
impl<'b> Engine<'b> {
pub fn update<A>(&mut self, app: &mut A)
where for<'a> A: UpdateWithContext<Context<'a, 'b>>
{
let context = Context { engine: self };
app.update(context);
}
pub fn start<A>(&mut self, app: A)
where for<'a> A: StartableApplication<'a, 'b>
{
let mut app = app.start(self);
loop {
self.update(&mut app);
}
}
}
fn main() {
println!("Hello, world!");
}
Error:
src/main.rs:38:18: 38:24 error: the trait bound `for<'a> <A as StartableApplication<'_, '_>>::Application: UpdateWithContext<Context<'a, '_>>` is not satisfied [E0277]
src/main.rs:38 self.update(&mut app);
^~~~~~
src/main.rs:38:18: 38:24 help: run `rustc --explain E0277` to see a detailed explanation
src/main.rs:38:18: 38:24 help: consider adding a `where for<'a> <A as StartableApplication<'_, '_>>::Application: UpdateWithContext<Context<'a, '_>>` bound
What I do wrong?
If I understood some idea wrong please correct me.
Take a look at the following:
use std::marker::PhantomData;
pub trait StartableApplication<'a, 'b>
where 'b: 'a
{
type Application: UpdateWithContext<Context<'a, 'b>>;
fn start(self, engine: &mut Engine<'b>) -> Self::Application;
}
pub trait UpdateWithContext<Context> {
fn update(&mut self, context: Context);
}
pub struct Context<'a, 'b>
where 'b: 'a
{
pub engine: &'a mut Engine<'b>,
}
pub struct Engine<'a> {
phantom_data: PhantomData<&'a mut i32>,
}
impl<'b> Engine<'b> {
pub fn update<'a, A>(&'a mut self, app: &mut A)
where 'b: 'a, A: UpdateWithContext<Context<'a, 'b>>,
{
let context = Context { engine: self };
app.update(context);
}
pub fn start<A>(&mut self, app: A)
where for<'a> A: StartableApplication<'a, 'b>,
{
let mut app = app.start(self);
loop {
self.update(&mut app);
}
}
}
fn main() {
println!("Hello, world!");
}
This gets past type-checking. Note that rustc tends to give low-quality error messages if you're doing complicated things with lifetimes.
Thanks a lot.
Could you please explain me what does <'a> means for update
'a
is a generic parameter ( Lifetimes ) . What exactly is your question about it?
What type context
in update
has?
I'm tempted to say you're diving in over your head if you can't answer that question at least partially... often, it's a lot easier to use a bunch of Rc
and RefCell
rather than try to create structures and traits where multiple lifetimes are involved.
Anyway, the usage of 'a
in update()
makes the lifetime explicit, and ties the lifetime of self
to the lifetime of the UpdateWithContext
trait. This is necessary because StartableApplication
takes an explicit lifetime 'a
, and so StartableApplication::Application
only implements UpdateWithContext
for a specific lifetime.
Taking another look, that might be confusing because it's not actually what you want. Maybe something more like this is closer?
use std::marker::PhantomData;
pub trait StartableApplication<'b>
{
type Application: for<'a> UpdateWithContext<Context<'a, 'b>>;
fn start(self, engine: &mut Engine<'b>) -> Self::Application;
}
pub trait UpdateWithContext<Context> {
fn update(&mut self, context: Context);
}
pub struct Context<'a, 'b>
where 'b: 'a
{
pub engine: &'a mut Engine<'b>,
}
pub struct Engine<'a> {
phantom_data: PhantomData<&'a mut i32>,
}
impl<'b> Engine<'b> {
pub fn update<A>(&mut self, app: &mut A)
where A: for<'a> UpdateWithContext<Context<'a, 'b>>,
{
let context = Context { engine: self };
app.update(context);
}
pub fn start<A>(&mut self, app: A)
where A: StartableApplication<'b>,
{
let mut app = app.start(self);
loop {
self.update(&mut app);
}
}
}
fn main() {
println!("Hello, world!");
}
What's the difference between
pub trait StartableApplication<'a, 'b>
where 'b: 'a
{
type Application: UpdateWithContext<Context<'a, 'b>>;
fn start(self, engine: &mut Engine<'b>) -> Self::Application;
}
and
pub trait StartableApplication<'b>
{
type Application: for<'a> UpdateWithContext<Context<'a, 'b>>;
fn start(self, engine: &mut Engine<'b>) -> Self::Application;
}
You example works, but mine not. I don't see any difference.
I added use and it does not compiles:
use std::marker::PhantomData;
pub trait StartableApplication<'b>
{
type Application: for<'a> UpdateWithContext<Context<'a, 'b>>;
fn start(self, engine: &mut Engine<'b>) -> Self::Application;
}
pub trait UpdateWithContext<Context> {
fn update(&mut self, context: Context);
}
pub struct Context<'a, 'b>
where 'b: 'a
{
pub engine: &'a mut Engine<'b>,
}
pub struct Engine<'a> {
phantom_data: PhantomData<&'a mut i32>,
}
impl<'b> Engine<'b> {
pub fn update<A>(&mut self, app: &mut A)
where A: for<'a> UpdateWithContext<Context<'a, 'b>>,
{
let context = Context { engine: self };
app.update(context);
}
pub fn start<A>(&mut self, app: A)
where A: StartableApplication<'b>,
{
let mut app = app.start(self);
loop {
self.update(&mut app);
}
}
}
struct MyStartableApplication;
impl<'a> StartableApplication<'a> for MyStartableApplication {
type Application = MyApplication;
fn start(self, engine: &mut Engine<'a>) -> Self::Application {
MyApplication::new(engine)
}
}
struct MyApplication;
impl MyApplication {
fn new<'a>(engine: &mut Engine<'a>) -> Self {
MyApplication
}
}
impl<'a, 'b> UpdateWithContext<Context<'a, 'b>> for MyApplication
where 'b: 'a
{
fn update(&mut self, context: Context<'a, 'b>) {
}
}
fn main() {
println!("Hello, world!");
}
I have solved this. This is because I added where 'b: 'a
in impl UpdateWithContext for MyApplication
Ugh, yes, you're right, type Application: for<'a> UpdateWithContext<Context<'a, 'b>>;
isn't usable. Not sure what I was thinking. And here I thought I knew what I was doing with lifetimes.
Your whole problem becomes a lot simpler if you get rid of the Context struct. The way you construct the context makes things really awkward because you can't really express the lifetimes the way you want to in Rust.
Example:
use std::marker::PhantomData;
pub trait StartableApplication<'b>
{
type Application: UpdateWithContext<Engine<'b>>;
fn start(self, engine: &mut Engine<'b>) -> Self::Application;
}
pub trait UpdateWithContext<Engine> {
fn update(&mut self, context: &mut Engine);
}
pub struct Engine<'a> {
phantom_data: PhantomData<&'a mut i32>,
}
impl<'b> Engine<'b> {
pub fn update<A>(&mut self, app: &mut A)
where A: UpdateWithContext<Engine<'b>>,
{
app.update(self);
}
pub fn start<A>(&mut self, app: A)
where A: StartableApplication<'b>,
{
let mut app = app.start(self);
loop {
self.update(&mut app);
}
}
}
struct MyStartableApplication;
impl<'a> StartableApplication<'a> for MyStartableApplication {
type Application = MyApplication;
fn start(self, engine: &mut Engine<'a>) -> Self::Application {
MyApplication::new(engine)
}
}
struct MyApplication;
impl MyApplication {
fn new<'a>(engine: &mut Engine<'a>) -> Self {
MyApplication
}
}
impl<'a, 'b> UpdateWithContext<Engine<'b>> for MyApplication
where 'b: 'a
{
fn update(&mut self, context: &mut Engine<'b>) {
}
}
fn main() {
println!("Hello, world!");
}