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

Я новичок в Rust и хочу создать простое приложение для рендеринга фракталов, подобное Мандельброту. Фракталы отображаются в X11-окне. Окно X11 создано с помощью xcb crate (версия 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.


person fsasm    schedule 19.07.2016    source источник
comment
Вероятно, дубликат stackoverflow.com/q/32300132   -  person Shepmaster    schedule 20.07.2016
comment
@malbarbo Я добавил ссылку на пример   -  person fsasm    schedule 20.07.2016
comment
@fsasm ожидается, что вопрос, который вы задаете здесь, будет минимальным воспроизводимым примером. Предоставление внешней ссылки на код с реальной проблемой не поможет никому в будущем и может быть причиной закрыть этот вопрос, поскольку он не предоставляет достаточно информации для воспроизведения.   -  person Shepmaster    schedule 20.07.2016


Ответы (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
    }
}

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

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

    // no semicolon here
    (0, 0, Vec::new())
}
person Maciej    schedule 19.07.2016
comment
Я пробовал использовать блоки, как вы предложили, но все равно получаю те же сообщения об ошибках. - person fsasm; 20.07.2016
comment
Я думаю, что ваше решение не работает, потому что setup привязан к времени жизни conn и, следовательно, заимствование существует в течение всего времени жизни conn. - person fsasm; 20.07.2016
comment
Можете куда-нибудь вставить модифицированный код? setup действительно привязан к времени жизни conn, заимствование должно закончиться, когда setup выйдет за пределы области действия. Из того, что я вижу, все остальные значения в структуре равны Copy, поэтому ничто другое не может заимствовать conn. - person Maciej; 20.07.2016
comment
Измените две строки с setup и screen на это: let screen = {let setup = conn.get_setup(); setup.roots().nth(screen_nr as usize).unwrap()}; - person fsasm; 20.07.2016
comment
Да, этого не пойдет, потому что у вас по-прежнему будут screen и setup в том же блоке, что и conn. Дело в том, что вы делаете с ними все, что вам нужно, внутри блока и перемещаете только те значения, которые вам нужны, за пределы (window, gc и fb). - person Maciej; 20.07.2016
comment
Спасибо. Теперь это работает, хотя fb - это всего лишь вектор, и поэтому только window и gc нужно переместить за пределы блока. Я не заметил, что время жизни также влияет на screen и root, поскольку компилятор жаловался только на setup. - person fsasm; 20.07.2016