Lifetime trouble with Trait object (Solved)


#1

Hi everyone,

I’ve been working on an emulator for awhile now and am very close to finishing the cpu part and am having a bit of trouble with a cryptic lifetime error.

Background -
This is a 68k cpu emulator based on r68k and Musashi. r68k had everything pretty much done just extracted everything and refactored some stuff to look cleaner. Main change is I pass a Bus trait everywhere so the user (myself) only has to implement the Bus trait (should be like a memory map read/write).

My error is when trying to add the opcode functions as pointers into the 64k opcode table I’m getting “expected concrete lifetime, found bound lifetime parameter” on all my opcode additions.

Here is an example of the error.

All code is included at https://github.com/mattbettcher/m68k

I know I’m using the feature ‘universal_impl_trait’, I feel that makes the code look cleaner. I did try to add explicit lifetimes everywhere, but still got the same error, so I don’t think it’s that.

Before I throw in the towel on this Trait Object as an interface I’d love to hear any help or suggestions. My only alternative is callbacks? or forget the separate crate and just hard code the cpu in. My hope was for something others could use again.

Here is my current toolchain.
current toolchain


#2

It would be helpful if you’d include the relevant variables and types that result in the error in your post so as to avoid having to look through thousands of lines of code.


#3

Sorry,

These are the function pointers types.

pub type Result<T> = result::Result<T, Exception>;
pub type Handler = fn(&mut M68k, &mut Bus) -> Result<u32>;

Errors occur in this macro invocation.

macro_rules! op_entry {
    ($mask:expr, $matching:expr, $handler:ident) => (OpcodeHandler { mask: $mask, matching: $matching, handler: $handler, })
}

All this does is fill in this struct -

struct OpcodeHandler {
    mask: u32,
    matching: u32,
    handler: Handler,
}

A vector of these is returned and represents every instruction.
I loop over every possible u16 and check for matches and add them to a Vec<Handler>

All the code is at the bottom of optable.rs as is the macro (although it’s at line 142)

The picture I posted above is the only error times about 1500.


#4

Reproduction without universal_impl_trait:

fn foo<T: AsMut<u8> + ?Sized>(_: &mut T) {}

fn main() {
    let mut bar: Vec<for<'a> fn(&mut (AsMut<u8> + 'a))> = vec![];
    bar.push(foo);
}

I’m not sure why you can’t get a higher-kinded lifetime version of foo. One way to fix it is to remove the higher kindedness and add an explicit lifetime parameter to your Handler type:

fn foo<T: AsMut<u8> + ?Sized>(_: &mut T) {}

type FnVec<'a> = Vec<fn(&mut (AsMut<u8> + 'a))>;

fn main() {
    let mut bar: FnVec = vec![];
    bar.push(foo);
}

Alternatively, if you’re going to be using trait objects, just define all your handlers to take a trait object as well? (Instead of a generic)


#5

That looks like an impossible bound - it’s saying the caller picks the lifetime of the reference but callee picks the lifetime the trait object is valid over.


#6

Thanks so much!

Here is a playground of what I came up with based on your input!

Playground Example

I filled out all the types and made sure I could call the function pointer and impl the Bus. I will keep messing around with this and keep you updated!


#7

Thanks so much for the help guys! This has fixed the error I was having!


#8

Ok, so I got everything working then started working on my step function and now I’m getting a Sized error again.

Playground

I’ve tried to minimize code so it’s easy to follow, but still represents my actual use case.

Notice how I can call the function pointer no problem outside of the impl (line 52) but when I try to call it in my step function (uncomment line 18) I get the same T doesn’t have a constant size at compile time. I’m lost on this. Any help is much appreciated.


#9

Since you’re turning the bus generic type param into a trait object, you need to drop the ?Sized bound on step. In addition, you need to indicate that the object is valid over lifetime 'a: https://play.rust-lang.org/?gist=08000c0f98b13fbbcde7849cb70bed9c&version=stable


#10

Awesome! Thanks so much! Makes so much sense now.