Встраивание переменных с временами жизни в структуру

Я новичок в Rust и хочу сделать простое приложение для рендеринга фракталов, таких как Мандельброт. Фракталы отображаются в X11-окне. Окно X11 сделано с ящиком xcb (версия 0.7.4).

Я хочу инкапсулировать все, что нужно для окна в структуре.

extern crate xcb;
use xcb::base::*;

struct FbWindow {
    conn: Connection,
    window: u32,
    gc: u32,
    width: u16,
    height: u16,
    fb: Vec<u8>
}

В моем new функция для структуры Мне нужен объект установки из соединения, который каким-то образом имеет то же время жизни, что и объект соединения.

impl FbWindow {
    fn new(width: u16, height: u16) -> FbWindow 
    {
        let (conn, screen_nr) = Connection::connect(None).unwrap();
        let setup = conn.get_setup();
        let screen = setup.roots().nth(screen_nr as usize).unwrap();
        let root = screen.root();

        /* Create GC - graphics context */
        let gc = conn.generate_id();
        let gc_values = [
            (xcb::GC_FOREGROUND, screen.black_pixel()),
            (xcb::GC_GRAPHICS_EXPOSURES, 0)
        ];
        xcb::create_gc(&conn, gc, root, &gc_values);

        /* Create window */
        let window = conn.generate_id();
        let window_values = [
            (xcb::CW_BACK_PIXEL, screen.black_pixel()),
            (xcb::CW_EVENT_MASK, xcb::EVENT_MASK_EXPOSURE | xcb::EVENT_MASK_KEY_PRESS)
        ];
        xcb::create_window(&conn, xcb::COPY_FROM_PARENT as u8, window, root,
            0, 0, width, height, 1, xcb::WINDOW_CLASS_INPUT_OUTPUT as u16,
            screen.root_visual(), &window_values
        );
        xcb::map_window(&conn, window);

        /* Create the framebuffer */
        let mut fb : Vec<u8> = vec![0; (width as usize) * (height as usize) * 4];

        FbWindow {
            conn: conn, 
            window: window,
            gc: gc,
            width: width,
            height: height,
            fb: fb
        }
    }
}

Компилятор не позволяет мне переместить объект соединения в объект структуры, который должен быть возвращен new, Я тоже пробовал с добавлением setup в структуру, но это не помогает. Компилятор выдает следующую ошибку с кодом сверху:

src/main.rs:46:19: 46:23 error: cannot move out of `conn` because it is borrowed [E0505]
src/main.rs:46             conn: conn, 
                                 ^~~~
src/main.rs:18:21: 18:25 note: borrow of `conn` occurs here
src/main.rs:18         let setup = conn.get_setup();
                                   ^~~~

Поиск документации о типе установки, показывает

type Setup<'a> = StructPtr<'a, xcb_setup_t>;

Я действительно плохо знаком с ржавчиной и концепцией жизни, и это все еще смущает меня, но, насколько я понимаю setup имеет то же время жизни, что и conn и компилятор отказывается двигаться из-за заимствования в setup,

Как мне заставить код работать как задумано?

Редактировать: код основан на примерах из хранилища ящиков Edit2: полный исходный код для new,

1 ответ

Решение

Мы все почесали головы по этому поводу. По большей части, компилятор говорит вам, что не так.

В строке 18 вы занимаетесь conn:

let setup = conn.get_setup();

Большинство методов принимают &self или же &mut self в качестве первого аргумента, таким образом заимствуя объект, к которому они призваны. Если метод ничего не возвращает, заем заканчивается в конце его области действия. Это не тот случай, когда setup использует всю жизнь ('a в Setup<'a>), что продлит срок заимствования. Это обычно не мешает вам, так как вы можете иметь столько неизменных заимствований, сколько захотите, но если у вас есть хотя бы одна, принадлежащая переменная не может быть перемещена.

Так! Пока setup существует, компилятор не позволит вам двигаться conn, Чтобы это исправить, вам нужно убедиться, что программа установки "умирает", прежде чем создавать структуру. Простой способ сделать это - обернуть его в блок следующим образом:

fn new(width: u16, height: u16) -> FbWindow 
{
    let (conn, screen_nr) = Connection::connect(None).unwrap();

    // because of block scoping, you might want to declare variables here
    let gc: u32;
    ...

    {
        // Borrowing `conn` here...
        let setup = conn.get_setup();
        let screen = setup.roots().nth(screen_nr as usize).unwrap();
        ...

        // Assign to the on the outer scope
        gc = ...;

        // All variables declared within this block will drop here,
    }

    // `conn` is not borrowed anymore!

    FbWindow {
        conn: conn,
        window: window,
        gc: gc,
        width: width,
        height: height,
        fb: fb
    }
}

В качестве альтернативы, вместо объявления неинициализированных переменных вы можете использовать тот факт, что блок является выражением в Rust и разрешается до последней строки в нем. Вы можете упаковать вещи в кортеж и разобрать его с помощью сопоставления с шаблоном:

let (window, gc, fb) = {
    ...

    // no semicolon here
    (0, 0, Vec::new())
}
Другие вопросы по тегам