How do I copy/clone a struct that derives neither?

耗尽温柔 提交于 2021-01-04 07:07:17

问题


I'm trying to do some game programming with Piston, but i'm struggling with opengl_graphics::Texture, since it does not derive Copy or Clone.

extern crate piston_window;
extern crate piston;
extern crate graphics;
extern crate opengl_graphics;

use opengl_graphics::Texture as Tex;
use piston_window::*;
use std::path::Path;
use opengl_graphics::GlGraphics;

#[derive(PartialEq)]
enum ObjectType {
    Blocking,
    Passing,
}
struct Object {
    sprite: Tex,
    obj_type: ObjectType,
    position: Position,
}
struct Game {
    gl: GlGraphics,
    images: Vec<Object>,
    player: Player,
}
struct Player {
    sprite: Tex,
    position: Position,
}
struct Position {
    x: i32,
    y: i32,
}

impl Game {
    fn render(&mut self, args: &RenderArgs) {

        let iter = self.images.iter();
        let player = &self.player;
        self.gl.draw(args.viewport(), |c, g| {

            clear([1.0, 1.0, 1.0, 1.0], g);

            for img in iter {
                let pos = img.get_position();
                let transform = c.transform.trans(((pos.x * 64)) as f64, ((pos.y * 64)) as f64);
                image(img.get_sprite(), transform, g);
            }
            image(player.get_sprite(),
                  c.transform.trans((player.get_position().x * 64) as f64,
                                    (player.get_position().y * 64) as f64),
                  g);
        });
    }


    fn update(&mut self, args: &UpdateArgs) {}
}

The main game loop:

fn main() {
    let (width, height) = (64*10, 64*10);
    let opengl = OpenGL::V3_2;
    let mut window: PistonWindow =
        WindowSettings::new("piston", (width, height))
        .exit_on_esc(true)
        .opengl(opengl)
        .build()
        .unwrap();
    window.hide();
    println!("Loading...");
    let mut player = Player { sprite: Tex::from_path(&Path::new(
                            "./assets/player_n.png")).unwrap(),
                            position: Position { x: 3, y: 3 },
    };

    let mut game = Game {
        gl: GlGraphics::new(opengl),
        images: Vec::new(),
        player: player,
    };

    for i in 0..10 {
        for j in 0..10 {
            if i == 0 || i == 9 || j == 0 || j == 9 {
                let obj = Object { sprite: Tex::from_path(&Path::new(
                        "./assets/wall.png")).unwrap(),                                                 
                    obj_type: ObjectType::Blocking,
                        position: Position { x: i, y: j },
                };
                game.images.push(obj);

            } else {
                let obj = Object { sprite: Tex::from_path(&Path::new(
                        "./assets/floor.png")).unwrap(),                                                
                    obj_type: ObjectType::Passing,
                        position: Position { x: i, y: j },
                };
                game.images.push(obj);
            }
        }
    }
    window.show();
    while let Some(e) = window.next() {

        if let Some(Button::Keyboard(key)) = e.press_args() {
            let mut pos = game.player.position.clone();
            let mut spr: Option<Tex> = None;          
            match key {
                Key::Up => { pos.y -= 1; spr = Some(Tex::from_path(&Path::new(
                        "./assets/player_n.png")).unwrap()); },
                Key::Down => { pos.y += 1; spr = Some(Tex::from_path(&Path::new(
                        "./assets/player_s.png")).unwrap()); },
                Key::Left => { pos.x -= 1; spr = Some(Tex::from_path(&Path::new(
                        "./assets/player_w.png")).unwrap()); },              
                Key::Right => { pos.x += 1; spr = Some(Tex::from_path(&Path::new(
                        "./assets/player_e.png")).unwrap()); },
                _ => (),
            }
            for elem in game.images.iter() {
                if pos.x == elem.position.x && pos.y == elem.position.y && elem.obj_type == ObjectType::Passing {
                    game.player.position = pos;

                    game.player.sprite = spr.clone().unwrap();

                }
            }
        }        
        if let Some(r) = e.render_args() {
            game.render(&r);       
        }
        if let Some(u) = e.update_args() {
            game.update(&u); 
        }
    }
}

Produces the error:

error: no method named `clone` found for type `std::option::Option<opengl_graphics::Texture>` in the current scope
   --> src/main.rs:159:46
159 |                     game.player.sprite = spr.clone().unwrap();
    |                                              ^^^^^
    |
    = note: the method `clone` exists but the following trait bounds were not satisfied: `opengl_graphics::Texture : std::clone::Clone`

I understand why I get this error, since opengl_graphics::Texture doesn't derive Copy I can't clone Option<opengl_texture>. What workaround is there for this?

I tried messing around with references but that didn't work.


回答1:


How do I copy/clone a struct that derives neither?

You don't. The only thing you can do is take some kind of reference to it.


In this case, it is a very good thing that the library has chosen to not implement Clone or Copy. If you were able to clone the structure, you'd be allocating a lot of memory frequently and needlessly. Instead, the library has forced you to think about when you allocate that memory. One of the solutions is to load all the textures at application startup and reference them:

Change your structures to hold references:

#[derive(PartialEq)]
enum ObjectType {
    Blocking,
    Passing,
}

struct Object<'a> {
    sprite: &'a Tex,
    obj_type: ObjectType,
    position: Position,
}

struct Game<'a> {
    gl: GlGraphics,
    images: Vec<Object<'a>>,
    player: Player<'a>,
}

struct Player<'a> {
    sprite: &'a Tex,
    position: Position,
}

#[derive(Copy, Clone, PartialEq)]
struct Position {
    x: i32,
    y: i32,
}

struct Textures {
    player_n: Tex,
    player_s: Tex,
    player_e: Tex,
    player_w: Tex,
    wall: Tex,
    floor: Tex,
}

Load the textures early on in main. Note that there's no need to use Path explicitly, as it takes AsRef<Path>:

let textures = Textures {
    player_n: Tex::from_path("./assets/player_n.png").unwrap(),
    player_s: Tex::from_path("./assets/player_s.png").unwrap(),
    player_e: Tex::from_path("./assets/player_e.png").unwrap(),
    player_w: Tex::from_path("./assets/player_w.png").unwrap(),
    wall: Tex::from_path("./assets/wall.png").unwrap(),
    floor: Tex::from_path("./assets/floor.png").unwrap()
};

Then pass references to those textures:

match key {
    Key::Up => {
        pos.y -= 1;
        spr = Some(&textures.player_n)
    }
    Key::Down => {
        pos.y += 1;
        spr = Some(&textures.player_s)
    }
    Key::Left => {
        pos.x -= 1;
        spr = Some(&textures.player_w)
    }
    Key::Right => {
        pos.x += 1;
        spr = Some(&textures.player_e)
    }
    _ => (),
}
for elem in game.images.iter() {
    if pos == elem.position && elem.obj_type == ObjectType::Passing {
        game.player.position = pos;

        if let Some(spr) = spr {
            game.player.sprite = spr;
        }
    }
}

Note that this also consolidates the places that errors can occur. There's no longer an unwrap inside the guts of the loop.

I was unable to get your code to finish compiling as the code is not complete but this should help get started:

error: no method named `render` found for type `Game<'_>` in the current scope
   --> src/main.rs:122:18
    |
122 |             game.render(&r);
    |                  ^^^^^^
    |
    = help: items from traits can only be used if the trait is implemented and in scope; the following trait defines an item `render`, perhaps you need to implement it:
    = help: candidate #1: `piston_window::RenderEvent`

error: no method named `update` found for type `Game<'_>` in the current scope
   --> src/main.rs:125:18
    |
125 |             game.update(&u);
    |                  ^^^^^^
    |
    = help: items from traits can only be used if the trait is implemented and in scope; the following traits define an item `update`, perhaps you need to implement one of them:
    = help: candidate #1: `piston_window::UpdateEvent`
    = help: candidate #2: `piston_window::<unnamed>::UpdateTexture`
    = help: candidate #3: `deflate::checksum::RollingChecksum`
    = help: candidate #4: `cocoa::appkit::NSOpenGLContext`
    = help: candidate #5: `cocoa::appkit::NSOpenGLContext`

In some cases, you may be able to wrap your types in an Rc or Arc and clone that. Cloning a Rc/Arc only increments a reference counter, regardless of the implementation of Clone for the underlying type (or the absence of such an implementation).



来源:https://stackoverflow.com/questions/42370839/how-do-i-copy-clone-a-struct-that-derives-neither

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!