Вам необходимо отделить создание вашей структуры 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
вам нужно что-то еще.
Возможно, лучше, если бы на самом высоком уровне 'Pen' хранился' Box'; как она есть, можно написать 'let x = Pen :: new(); пусть y = * x; 'для перемещения' Pen' в память. – huon
@dbaupp, но тогда обратный вызов Rust не будет иметь доступа к какому-либо из функций в 'Pen'. – o11c
Хм, если у вас есть 'Pen {p: Box}' Я думаю, вы можете безопасно выставлять '& InternalPen' (если' InternalPen' является аффинным), но даже разоблачение '& mut InternalPen' небезопасно: кто-то может называть' swap' для обменять два 'InternalPen'. Как только вы начнете подвергать влиянию зависимых от местоположения значений, внешне трудно поддерживать безопасность. То есть, было бы нормально иметь 'FnMut <(& InternalPen, EventDetails),()>'. Если у вас все в порядке, если у вас есть риск небезопасности памяти, то просмотр «& mut InternalPen», вероятно, прекрасен (кажется, редко, что люди будут называть «swap» такими значениями). –
huon