2014-10-14 2 views
2

Я обертываю объекты из библиотеки C, которая генерирует события на объекте. Чтобы сопоставить событие низкого уровня с высокоуровневым, мне нужно передать адрес объекта высокого уровня.Как «заставлять клонировать для» тип, который нельзя разрешить для перемещения?

Все это работает.

mod c 
{ 
    enum Pen; // opaque struct 
} 

struct Pen 
{ 
    pen: *mut c::Pen, 
    event_hook: Option<Box<FnMut<(&mut Pen, EventDetails),()>>>, 
} 

impl Pen 
{ 
    fn new() -> Box<Pen> 
    { 
     let rv = box Pen{pen: ..., event_hook: None}; 
     ... set up hook to use rv's raw address in a callback and forward to event_hook ...; 
     rv 
    } 
} 

impl Drop for Pen 
{ 
    fn drop(&mut self) 
    { 
     ...; 
    } 
} 

Проблема возникает, когда я пытаюсь установить impl Clone. Я не могу impl Clone for Box<Pen>, потому что это противоречит реализации в liballoc. Я не могу impl Clone for Pen, потому что адрес изменился после того, как я установил крючки низкого уровня.

Способность клонировать этот тип весьма важна. Я мог бы impl Pen дать функции названных.clone() и .clone_from(), что на самом деле возвращение коробки, но это будет препятствовать любому прохождению этого объекта в функцию, которая требует <T: Clone>

+0

Возможно, лучше, если бы на самом высоком уровне 'Pen' хранился' Box'; как она есть, можно написать 'let x = Pen :: new(); пусть y = * x; 'для перемещения' Pen' в память. – huon

+0

@dbaupp, но тогда обратный вызов Rust не будет иметь доступа к какому-либо из функций в 'Pen'. – o11c

+0

Хм, если у вас есть 'Pen {p: Box }' Я думаю, вы можете безопасно выставлять '& InternalPen' (если' InternalPen' является аффинным), но даже разоблачение '& mut InternalPen' небезопасно: кто-то может называть' swap' для обменять два 'InternalPen'. Как только вы начнете подвергать влиянию зависимых от местоположения значений, внешне трудно поддерживать безопасность. То есть, было бы нормально иметь 'FnMut <(& InternalPen, EventDetails),()>'. Если у вас все в порядке, если у вас есть риск небезопасности памяти, то просмотр «& mut InternalPen», вероятно, прекрасен (кажется, редко, что люди будут называть «swap» такими значениями). – huon

ответ

0

Вы можете дать Pen в NoCopy поле, которое будет препятствовать его из когда-либо неявно скопирован.

Не уверен, что это то, о чем вы просите.

+0

Он все еще может быть перемещен в память, что приведет к аннулированию любых внутренних указателей, которые объект имеет для себя. – huon

1

Вам необходимо отделить создание вашей структуры Pen от перехвата и снятия соединения.

use std::kinds::marker::NoCopy; 

mod c 
{ 
    pub struct Pen; // opaque struct 
} 

struct Pen 
{ 
    pen: *mut c::Pen, 
    //event_hook: Option<Box<FnMut<(&mut Pen, EventDetails),()>>>, // FIXME: this type does not compile 
    _no_copy: NoCopy 
} 

struct PenHookInner { 
    event_hook: Option<()> // TODO: maybe event_hook should move here from Pen? 
} 

struct PenHook<'a> { 
    pen: &'a Pen, 
    inner: PenHookInner, 
} 

impl Pen 
{ 
    fn new() -> Pen 
    { 
     let rv = Pen { pen: 0 as *mut c::Pen /*, event_hook: None*/, _no_copy: NoCopy }; 
     rv 
    } 

    fn hook(&self) -> PenHook { 
     // TODO: set up hook to use self.pen's raw address in a callback and forward to event_hook 
     PenHook { 
      pen: self, 
      inner: PenHookInner { event_hook: None } 
     } 
    } 
} 

impl Drop for PenHookInner { 
    fn drop(&mut self) { 
     // TODO: remove the hook 
    } 
} 

fn main() { 
    let pen = Pen::new(); 
    let pen_hook = pen.hook(); 
    //drop(pen); //~ error: cannot move out of `pen` because it is borrowed 
} 

Pen содержит NoCopy поля, чтобы сделать структуру не неявно копируемой. Если вам нужно реализовать Drop, чтобы освободить «родную» ручку, то вам не нужно поле NoCopy. (Типы, которые реализуют Drop неявно не Copy.)

Когда PenHook создан, он заимствует ссылку на Pen. Это предотвращает перемещение или опускание Pen, пока существует PenHook. Пока существует объект PenHook, Pen не может быть перемещен или удален.

Если Pen не имеет поля NoCopy, он все равно может быть неявным образом скопирован: удалите его, и вы увидите, что компилируется строка drop(pen);. Это потому, что drop() получает копию pen и падает, что. Поскольку Pen не использует Drop в этом примере, это не-op.

В настоящее время мы применяем Drop для PenHookInner. PenHookInner будет содержать данные, необходимые для удаления крюка. Мы не можем реализовать Drop по адресу PenHook непосредственно без #[unsafe_destructor], из-за issue 11406. Если нам не нужен доступ к заимствованному указателю drop(), мы можем использовать отдельную структуру, чтобы обойти ее чисто.

Я сделал Pen::new(), а не Pen, а не Box<Pen>, потому что нет причин поместить результат. Если вызывающий абонент хочет установить значение, он может использовать box на сайте вызова.

...Я собирался закончить открытый вопрос о том, какой тип будет реализовывать Clone, но затем я прочитал ваш комментарий о Pen, уходящем в следующую версию следующей библиотеки. Полагаю, вы хотели клонировать как ручку, так и крючок. Очевидно, что с этой структурой вы не сможете сделать это легко. Вы можете легко реализовать Clone на Pen, но для PenHook вам нужно что-то еще.

Смежные вопросы