简体   繁体   English

初始化结构内的方法指针

[英]Initialize method pointer inside struct

I am trying to write a NES emulator.我正在尝试编写 NES 模拟器。

I have created a lookup table that contains all the instructions for the 6502. Each Instruction contains the name of the instruction, a pointer to the method that is the instruction, a pointer to the address mode method, and how many cycles the instruction takes.我创建了一个查找表,其中包含 6502 的所有指令。每条指令都包含指令的名称、指向作为指令的方法的指针、指向地址模式方法的指针以及指令占用的周期数。

These are the structures for the CPU (Nes6502) and a structure for what each Instruction contains.这些是 CPU (Nes6502) 的结构和每个指令包含的结构。

pub struct Nes6502 {
    bus: Bus,

    /// Accumulator Register
    a: u8,
    /// X Register
    x: u8,
    /// Y Register
    y: u8,
    /// Stack pointer (pointers to a location on the bus)
    stkp: u8,
    /// Program counter
    pc: u16,
    /// Status Register
    status: u8,
    fetched: u8,
    addr_abs: u16,
    addr_rel: u16,
    opcode: u8,
    cycles: u8,
    lookup: Vec<Instruction>,
}

struct Instruction {
    name: String,
    operate: fn() -> u8,
    addrmode: fn() -> u8,
    cycles: u8,
}

But the issue I run into is when I try to initialize the lookup table for the instructions但是我遇到的问题是当我尝试为指令初始化查找表时

impl Nes6502 {
    fn new() -> Nes6502 {
        let mut ram= [0; MEM_SIZE]; 
        ram.iter_mut().for_each(|r| *r = 0x00);

        let mut nes = Nes6502 { 
            a: 0x00,
            x: 0x00,
            y: 0x00,
            stkp: 0x00,
            pc: 0x0000,
            status: 0x00,
            fetched: 0x00,
            addr_abs: 0x0000,
            addr_rel: 0x00,
            opcode: 0x00,
            cycles: 0,
            lookup: Vec::new(),

            bus: Bus {
                ram
            }   
        };

        nes.lookup = vec![
            Instruction{ name: String::from("BRK"), operate: || nes.BRK(), addrmode: || nes.IMM(), cycles: 7 } // ...
        ]
    
        nes
    }
    // ...
}

In the part where I initialize the lookup table with all the instructions I get this error在我使用所有指令初始化查找表的部分中,我收到此错误

error[E0308]: mismatched types
  --> src/nes_6502.rs:86:63
   |
86 | ...   Instruction{ name: String::from("BRK"), operate: || nes.BRK(), addrmode: || nes.IMM(), cycles: 7 }// ,Instruction{ name: String::fr...
   |                                                        ^^^^^^^^^^^^ expected fn pointer, found closure
   |
   = note: expected fn pointer `fn() -> u8`
                 found closure `[closure@src/nes_6502.rs:86:63: 86:75]`
note: closures can only be coerced to `fn` types if they do not capture any variables
  --> src/nes_6502.rs:86:66
   |
86 | ...   Instruction{ name: String::from("BRK"), operate: || nes.BRK(), addrmode: || nes.IMM(), cycles: 7 }// ,Instruction{ name: String::fr...
   |                                                           ^^^ `nes` captured here

I also tried initializing it like this我也尝试像这样初始化它

nes.lookup = vec![
            Instruction{ name: String::from("BRK"), operate: nes.BRK, addrmode: nes.IMM, cycles: 7 }
           ]

But that gave me this error但这给了我这个错误

error[E0615]: attempted to take value of method `BRK` on type `Nes6502`
  --> src/nes_6502.rs:86:67
   |
86 | ...   Instruction{ name: String::from("BRK"), operate: nes.BRK, addrmode: nes.IMM, cycles: 7 }// ,Instruction{ name: String::from("ORA"),...
   |                                                            ^^^ method, not a field
   |
help: use parentheses to call the method
   |
86 |              Instruction{ name: String::from("BRK"), operate: nes.BRK(), addrmode: nes.IMM, cycles: 7 }

I assume this is your 'minimal' example, as your code doesn't reproduce your issue directly:我认为这是您的“最小”示例,因为您的代码不会直接重现您的问题:

struct Bus {
    ram: [i32; 1024],
}

pub struct Nes6502 {
    bus: Bus,

    /// Accumulator Register
    a: u8,
    /// X Register
    x: u8,
    /// Y Register
    y: u8,
    /// Stack pointer (pointers to a location on the bus)
    stkp: u8,
    /// Program counter
    pc: u16,
    /// Status Register
    status: u8,
    fetched: u8,
    addr_abs: u16,
    addr_rel: u16,
    opcode: u8,
    cycles: u8,
    lookup: Vec<Instruction>,
}

struct Instruction {
    name: String,
    operate: fn() -> u8,
    addrmode: fn() -> u8,
    cycles: u8,
}

const MEM_SIZE: usize = 1024;

impl Nes6502 {
    fn new() -> Nes6502 {
        let mut ram = [0; MEM_SIZE];
        ram.iter_mut().for_each(|r| *r = 0x00);

        let mut nes = Nes6502 {
            a: 0x00,
            x: 0x00,
            y: 0x00,
            stkp: 0x00,
            pc: 0x0000,
            status: 0x00,
            fetched: 0x00,
            addr_abs: 0x0000,
            addr_rel: 0x00,
            opcode: 0x00,
            cycles: 0,
            lookup: Vec::new(),

            bus: Bus { ram },
        };

        nes.lookup = vec![
            Instruction {
                name: String::from("BRK"),
                operate: || nes.BRK(),
                addrmode: || nes.IMM(),
                cycles: 7,
            }, // ...
        ];

        nes
    }

    fn BRK(&self) -> u8 {
        todo!()
    }
    fn IMM(&self) -> u8 {
        todo!()
    }
    // ...
}
error[E0308]: mismatched types
  --> src/lib.rs:62:26
   |
62 |                 operate: || nes.BRK(),
   |                          ^^^^^^^^^^^^ expected fn pointer, found closure
   |
   = note: expected fn pointer `fn() -> u8`
                 found closure `[closure@src/lib.rs:62:26: 62:38]`
note: closures can only be coerced to `fn` types if they do not capture any variables
  --> src/lib.rs:62:29
   |
62 |                 operate: || nes.BRK(),
   |                             ^^^ `nes` captured here

error[E0308]: mismatched types
  --> src/lib.rs:63:27
   |
63 |                 addrmode: || nes.IMM(),
   |                           ^^^^^^^^^^^^ expected fn pointer, found closure
   |
   = note: expected fn pointer `fn() -> u8`
                 found closure `[closure@src/lib.rs:63:27: 63:39]`
note: closures can only be coerced to `fn` types if they do not capture any variables
  --> src/lib.rs:63:30
   |
63 |                 addrmode: || nes.IMM(),
   |                              ^^^ `nes` captured here

fn vs Fn / FnMut / FnOnce fnFn / FnMut / FnOnce

  • fn is a function pointer. fn是一个函数指针。 It is simply an address that points to some executable function.它只是一个指向某个可执行函数的地址。 No data can be attached to an fn .没有数据可以附加到fn
  • FnOnce is a trait that describes some object that can be executed at least once. FnOnce是一个特征,它描述了一些可以至少执行一次的对象。 The object gets consumed in the process of executing it.对象在执行过程中被消耗。
  • FnMut is a trait that describes some object that can be executed multiple times, but either has a side effect or changes its internal state in the process. FnMut是一个 trait,描述了一些可以多次执行的对象,但要么有副作用,要么在过程中改变其内部状态。 Mutable access to the object is required to execute it.执行它需要对对象的可变访问。 Every FnMut is also FnOnce .每个FnMut也是FnOnce
  • Fn is a trait that describes some object that can be executed many times without side effects or internal state changes. Fn是描述某些可以多次执行而不会产生副作用或内部状态更改的对象的特征。 A simple immutable reference is required to execute it.执行它需要一个简单的不可变引用。 Every Fn is also FnMut and FnOnce .每个Fn也是FnMutFnOnce

Fn , FnMut and FnOnce are all based on actual objects in memory, and not just pointers. FnFnMutFnOnce都基于内存中的实际对象,而不仅仅是指针。 Therefore those can carry data.因此那些可以携带数据。

Why is this important for your case?为什么这对您的案件很重要?

|| nes.BRK() || nes.BRK() is a closure that takes no argument, but uses the nes object. || nes.BRK()是一个不带参数的闭包,但使用nes对象。 Therefore it needs to store the reference to the nes object somewhere.因此它需要在某处存储对nes对象的引用。

So the first intuition would be to change fn to Box<dyn Fn> , like this:所以第一个直觉是将fn更改为Box<dyn Fn> ,如下所示:

struct Bus {
    ram: [i32; 1024],
}

pub struct Nes6502 {
    bus: Bus,

    /// Accumulator Register
    a: u8,
    /// X Register
    x: u8,
    /// Y Register
    y: u8,
    /// Stack pointer (pointers to a location on the bus)
    stkp: u8,
    /// Program counter
    pc: u16,
    /// Status Register
    status: u8,
    fetched: u8,
    addr_abs: u16,
    addr_rel: u16,
    opcode: u8,
    cycles: u8,
    lookup: Vec<Instruction>,
}

struct Instruction {
    name: String,
    operate: Box<dyn Fn() -> u8>,
    addrmode: Box<dyn Fn() -> u8>,
    cycles: u8,
}

const MEM_SIZE: usize = 1024;

impl Nes6502 {
    fn new() -> Nes6502 {
        let mut ram = [0; MEM_SIZE];
        ram.iter_mut().for_each(|r| *r = 0x00);

        let mut nes = Nes6502 {
            a: 0x00,
            x: 0x00,
            y: 0x00,
            stkp: 0x00,
            pc: 0x0000,
            status: 0x00,
            fetched: 0x00,
            addr_abs: 0x0000,
            addr_rel: 0x00,
            opcode: 0x00,
            cycles: 0,
            lookup: Vec::new(),

            bus: Bus { ram },
        };

        nes.lookup = vec![
            Instruction {
                name: String::from("BRK"),
                operate: Box::new(|| nes.BRK()),
                addrmode: Box::new(|| nes.IMM()),
                cycles: 7,
            }, // ...
        ];

        nes
    }

    fn BRK(&self) -> u8 {
        todo!()
    }
    fn IMM(&self) -> u8 {
        todo!()
    }
    // ...
}
error[E0506]: cannot assign to `nes.lookup` because it is borrowed
  --> src/lib.rs:59:9
   |
59 |         nes.lookup = vec![
   |         ^^^^^^^^^^ assignment to borrowed `nes.lookup` occurs here
...
62 |                 operate: Box::new(|| nes.BRK()),
   |                          ----------------------
   |                          |        |  |
   |                          |        |  borrow occurs due to use in closure
   |                          |        borrow of `nes.lookup` occurs here
   |                          cast requires that `nes` is borrowed for `'static`

error[E0597]: `nes` does not live long enough
  --> src/lib.rs:62:38
   |
62 |                 operate: Box::new(|| nes.BRK()),
   |                          ------------^^^-------
   |                          |        |  |
   |                          |        |  borrowed value does not live long enough
   |                          |        value captured here
   |                          cast requires that `nes` is borrowed for `'static`
...
69 |     }
   |     - `nes` dropped here while still borrowed

error[E0597]: `nes` does not live long enough
  --> src/lib.rs:63:39
   |
63 |                 addrmode: Box::new(|| nes.IMM()),
   |                           ------------^^^-------
   |                           |        |  |
   |                           |        |  borrowed value does not live long enough
   |                           |        value captured here
   |                           cast requires that `nes` is borrowed for `'static`
...
69 |     }
   |     - `nes` dropped here while still borrowed

error[E0505]: cannot move out of `nes` because it is borrowed
  --> src/lib.rs:68:9
   |
62 |                 operate: Box::new(|| nes.BRK()),
   |                          ----------------------
   |                          |        |  |
   |                          |        |  borrow occurs due to use in closure
   |                          |        borrow of `nes` occurs here
   |                          cast requires that `nes` is borrowed for `'static`
...
68 |         nes
   |         ^^^ move out of `nes` occurs here

This brings us to the next problem though: The closure outlives the current function, and therefore the compiler cannot guarantee that the nes object lives longer than the closure.这给我们带来了下一个问题:闭包比当前函数的寿命更长,因此编译器不能保证nes对象的寿命比闭包长。 Further, for as long as the closure borrows the nes object, no mutable reference to the nes can exist, which is probably not what you want.此外,只要闭包借用了nes对象,就不会存在对nes的可变引用,这可能不是您想要的。 I'm sure you want to modify the nes object.我确定您要修改nes对象。

So what you really want is to only borrow the nes object while executing the closure.所以你真正想要的是在执行闭包时只借用nes对象。 This can be achieved by passing the nes object into the closure as a function argument.这可以通过将nes对象作为函数参数传递给闭包来实现。

Once we have done that, the closure now no longer stores any references or objects, and we can use a plain fn function pointer again:一旦我们这样做了,闭包现在不再存储任何引用或对象,我们可以再次使用普通的fn函数指针:

struct Bus {
    ram: [i32; 1024],
}

pub struct Nes6502 {
    bus: Bus,

    /// Accumulator Register
    a: u8,
    /// X Register
    x: u8,
    /// Y Register
    y: u8,
    /// Stack pointer (pointers to a location on the bus)
    stkp: u8,
    /// Program counter
    pc: u16,
    /// Status Register
    status: u8,
    fetched: u8,
    addr_abs: u16,
    addr_rel: u16,
    opcode: u8,
    cycles: u8,
    lookup: Vec<Instruction>,
}

struct Instruction {
    name: String,
    operate: fn(&mut Nes6502) -> u8,
    addrmode: fn(&Nes6502) -> u8,
    cycles: u8,
}

const MEM_SIZE: usize = 1024;

impl Nes6502 {
    fn new() -> Nes6502 {
        let mut ram = [0; MEM_SIZE];
        ram.iter_mut().for_each(|r| *r = 0x00);

        let mut nes = Nes6502 {
            a: 0x00,
            x: 0x00,
            y: 0x00,
            stkp: 0x00,
            pc: 0x0000,
            status: 0x00,
            fetched: 0x00,
            addr_abs: 0x0000,
            addr_rel: 0x00,
            opcode: 0x00,
            cycles: 0,
            lookup: Vec::new(),

            bus: Bus { ram },
        };

        nes.lookup = vec![
            Instruction {
                name: String::from("BRK"),
                operate: |nes| nes.BRK(),
                addrmode: |nes| nes.IMM(),
                cycles: 7,
            }, // ...
        ];

        nes
    }

    fn BRK(&self) -> u8 {
        todo!()
    }
    fn IMM(&self) -> u8 {
        todo!()
    }
    // ...
}

Another minor nitpick另一个小挑剔

The name of your Instruction is most likely already known at compile time, so it saves time and resources to use &'static str instead:您的Instruction名称很可能在编译时就已经知道,因此使用&'static str可以节省时间和资源:

struct Instruction {
    name: &'static str,
    operate: fn(&mut Nes6502) -> u8,
    addrmode: fn(&Nes6502) -> u8,
    cycles: u8,
}

// ...

        nes.lookup = vec![
            Instruction {
                name: "BRK",
                operate: |nes| nes.BRK(),
                addrmode: |nes| nes.IMM(),
                cycles: 7,
            }, // ...
        ];

// ...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM