Cannot move out of borrowed context with glium

Hello,
I am fairly new to Rust and don't know the very advanced concepts yet. I'd like to make a simple framework for graphics using glium, however I've encountered a problem with the borrow checker.

main.rs

extern crate glium;

mod gfx;

use gfx::{Gfx, GfxContext, Color};

fn main() {
    let mut graphics = Gfx::new();
    graphics.start(|gfx: &mut GfxContext| {
        gfx.clear(Color(0.0, 1.0, 1.0, 1.0));
    });
}

gfx.rs

use glium::glutin;
use glium::Surface;

pub struct Color(pub f32, pub f32, pub f32, pub f32);

pub struct GfxContext {
    display: glium::Display,
    target: Option<glium::Frame>
}

pub struct Gfx {
    events_loop: glutin::EventsLoop,
    context: GfxContext
}

impl Gfx {
    pub fn new() -> Gfx {
        let events_loop = glutin::EventsLoop::new();
        let window = glutin::WindowBuilder::new();
        let context = glutin::ContextBuilder::new();
        let display = glium::Display::new(window, context, &events_loop).unwrap();

        Gfx {
            events_loop,
            context: GfxContext {
                display,
                target: None
            }
        }
    }

    pub fn start<D>(&mut self, drawfn: D) 
        where D: Fn(&mut GfxContext) {
        let mut closed = false;
        while !closed {
            self.events_loop.poll_events(|ev| {
                match ev {
                    glutin::Event::WindowEvent { event, .. } => match event {
                        glutin::WindowEvent::CloseRequested => closed = true,
                        _ => ()
                    },
                    _ => ()
                }
            });

            self.run_loop(&drawfn);
        }
    }

    fn run_loop<D>(&mut self, drawfn: D)
        where D: Fn(&mut GfxContext) {
        self.context.draw_frame(&drawfn);
    }
}

impl GfxContext {
    fn draw_frame<D>(&mut self, drawfn: D)
        where D: Fn(&mut GfxContext) {
        // self.target = Some(self.display.draw());
        // drawfn(self);
        self.target.unwrap().finish();
    }

    pub fn clear(&mut self, col: Color) {
        self.target.unwrap().clear_color(col.0, col.1, col.2, col.3);
    }
}

There are two compile-time errors at the end of gfx.rs.

error[E0507]: cannot move out of borrowed content
  --> src/gfx.rs:61:9
   |
61 |         self.target.unwrap().finish();
   |         ^^^^ cannot move out of borrowed content

error[E0507]: cannot move out of borrowed content
  --> src/gfx.rs:65:9
   |
65 |         self.target.unwrap().clear_color(col.0, col.1, col.2, col.3);
   |         ^^^^ cannot move out of borrowed content

I was unable to reproduce the error with a "sandbox", and I made all of this GfxContext mess while trying to avoid these errors. clear was previously a Gfx method.

unwrap moves the item out of the Option, this is not what you want if you want to use the value repeatedly, and is not possible if the value comes from a borrowed context.

I think it works if you use destructuring with a reference instead of unwrap, for example:

if let Some(ref mut target) = self.target {
    target.finish();
}
1 Like

Well, what do I do then if I want to assign a value to self.target? Just doing target = &mut self.display.draw() doesn't work (compilation error: cannot assign twice to immutable variable target)

Frame::finish() consumes the Frame, so you need to take() the value out of the Option:

// can also unwrap() the take() result
// to assert that it was Some
self.target.take().map(Frame::finish);

For clear(), you can use as_mut() to avoid moving out of borrowed content:

self.target.as_mut().unwrap().clear_color(...);
let target = self.display.draw();
// store target to where it’s needed

So I now have this:

fn draw_frame<D>(&mut self, drawfn: D)
    where D: Fn(&mut GfxContext) {
    let target = self.display.draw();
    self.target = Some(target);
    if let Some(ref mut target) = self.target {
        drawfn(self);
        target.finish();
    }
}

But it still won't work (cannot borrow *self as mutable more than once at a time), and I can't find a way to avoid using self to set the render target used by other functions in the struct.

You can’t borrow target in the match statement and then try to pass a mutable reference of self to another fn (closure in this case) while holding the mutable borrow to a part of self. But you don’t need that anyway - try:

self.target = Some(target);
drawnfn(self);
self.target.take().unwrap().finish();

Note you also need to take() the target in order to finish() it, as mentioned before. This would’ve been your next error :slight_smile:.

You may want to rethink the closure you take - perhaps pass it the Frame directly rather than temporarily storing it in self.

1 Like