2016-03-09 4 views
3

Я пишу обертку Rust вокруг библиотеки C, и, делая это, я пытаюсь использовать «оптимизацию указателей на нулевое значение», упомянутую в The Book, но я не могу найти хороший способ конвертировать Option<&T> в *const T и Option<&mut T> в *mut T как то, что они описывают.Convert Option <&mut T> to * mut T

Я действительно хочу, чтобы позвонить по телефону Some(&foo) as *const _. К сожалению, это не работает, поэтому следующая лучшая вещь, о которой я могу думать, - это черта на Option<T>, которая позволяет мне звонить Some(&foo).as_ptr(). Следующий код является рабочее определение и реализация для этого признака:

trait AsPtr<T> { 
    fn as_ptr(&self) -> *const T; 
} 

impl<'a, T> AsPtr<T> for Option<&'a T> { 
    fn as_ptr(&self) -> *const T { 
     match *self { 
      Some(val) => val as *const _, 
      None => ptr::null(), 
     } 
    } 
} 

Теперь, когда я могу назвать Some(&foo).as_ptr() получить *const _, я хочу, чтобы иметь возможность вызвать Some(&mut foo).as_ptr() получить *mut _. Ниже это новая черта я сделал, чтобы сделать это:

trait AsMutPtr<T> { 
    fn as_mut_ptr(&self) -> *mut T; 
} 

impl<'a, T> AsMutPtr<T> for Option<&'a mut T> { 
    fn as_mut_ptr(&self) -> *mut T { 
     match *self { 
      Some(val) => val as *mut _, 
      None => ptr::null_mut(), 
     } 
    } 
} 

Проблема, AsMutPtr черта не будет компилироваться. Когда я пытаюсь, я получаю следующее сообщение об ошибке:

wrapper.rs:115:15: 115:20 error: cannot move out of borrowed content [E0507] 
wrapper.rs:115   match *self { 
|       ^~~~~ 
wrapper.rs:115:15: 115:20 help: run `rustc --explain E0507` to see a detailed explanation 
wrapper.rs:116:18: 116:21 note: attempting to move value to here 
wrapper.rs:116    Some(val) => val as *mut _, 
|        ^~~ 
wrapper.rs:116:18: 116:21 help: to prevent the move, use `ref val` or `ref mut val` to capture value by reference 

Я не вижу, что изменилось между двумя чертами, которые вызывают его на провал - я не думаю, что добавление mut бы, что большая разница. Я попробовал добавить ref, где он предложил, но это просто вызывает другую ошибку (я могу вставить это здесь, если бы это было полезно), и я бы не ожидал, что это все равно.

Может кто-нибудь объяснить мне, почему черта AsMutPtr не работает?

ответ

3

К сожалению, написание признака осущ для &mut T вместо &Tделает сделать большую разницу. &mut T, в отличие от &T, не Copy, поэтому вы не можете извлечь его из общей ссылки непосредственно:

& &T  ---> &T 
& &mut T -/-> &mut T 

Это довольно естественно, - в противном случае наложения изменяемых ссылок будет возможно, что нарушает правила заимствования Руст.

Вы можете спросить, откуда этот внешний &. Это на самом деле происходит от &self в методе as_mut_ptr(). Если у вас есть неизменяемая ссылка на что-то, даже если что-то содержит изменяемые ссылки внутри него, вы не сможете использовать их для изменения данных позади них. Это также было бы нарушением семантики заимствования.

К сожалению, я не вижу возможности сделать это без опасностей. Вы должны иметь &mut T «по значению», чтобы отличить его от *mut T, но вы не можете получить его «по значению» через общую ссылку. Поэтому я предлагаю вам использовать ptr::read():

use std::ptr; 

impl<'a, T> AsMutPtr<T> for Option<&'a mut T> { 
    fn as_mut_ptr(&self) -> *mut T { 
     match *self { 
      Some(ref val) => unsafe { ptr::read(val) as *mut _ }, 
      None => ptr::null_mut(), 
     } 
    } 
} 

val здесь & &mut T из ref отборочных в шаблоне, поэтому ptr::read(val) возвращает &mut T, Aliasing изменяемых ссылок.Я думаю, что это нормально, если он сразу преобразуется в необработанный указатель и не просачивается, но даже если результат будет необработанным указателем, это все равно означает, что у вас есть два изменяемых указателя с псевдонимом. Вы должны быть очень осторожны с тем, что вы делаете с ними.

В качестве альтернативы, вы можете изменить AsMutPtr::as_mut_ptr() потреблять свою цель по значению:

trait AsMutPtr<T> { 
    fn as_mut_ptr(self) -> *mut T; 
} 

impl<'a, T> AsMutPtr<T> for Option<&'a mut T> { 
    fn as_mut_ptr(self) -> *mut T { 
     match self { 
      Some(value) => value as *mut T, 
      None => ptr::null_mut() 
     } 
    } 
} 

Однако, в этом случае Option<&mut T> будет потребляться as_mut_ptr(). Это может оказаться невозможным, если, например, этот Option<&mut T> хранится в структуре. Я не уверен, можно ли каким-то образом выполнить reborrowing вручную с Option<&mut T>, а не только &mut T (он не будет запускаться автоматически); если это возможно, то по значению as_mut_ptr(), вероятно, является лучшим общим решением.

+0

Благодарим вас за это объяснение. Мне до сих пор предстоит пройти долгий путь, прежде чем я полностью пойму чекер по заимствованию, и это очень помогает. – Brian

+0

«Я думаю, что это нормально, если он сразу преобразуется в необработанный указатель и не просачивается» → Я не уверен, что наличие двух активных псевдонимов '' '' '' '' '' '' '' '' 'наверняка кажется незаконным. Правило, которое я слышал, заключается в том, что вы никогда не должны смешивать '*' и '&', чтобы вы не попали в такие ситуации. – Veedrac

1

Будет ли это делать то, что вы ожидаете?

trait AsMutPtr<T> { 
    fn as_mut_ptr(self) -> *mut T; 
} 

impl<T> AsMutPtr<T> for Option<*mut T> { 
    fn as_mut_ptr(self) -> *mut T { 
     match self { 
      Some(val) => val as *mut _, 
      None => ptr::null_mut(), 
     } 
    } 
} 
+0

Это была опция 'Option <&mut T>' not 'Option <*mut T>'; вы близки! –

1

Проблема заключается в том, что вы читаете &mut из-за &, но &mut s не Copy так должны быть перемещены - и вы не можете выйти из константной ссылки. Это на самом деле объясняет мнение Владимира Матвеева о более поздних фундаментальных свойствах.

Это на самом деле относительно просто разрешено. Если вы можете прочитать *const _, вы можете прочитать *mut _. Оба имеют один и тот же тип, бар с надписью «будьте осторожны, это разделяется». Поскольку разыменования являются небезопасными в любом случае, на самом деле нет оснований останавливать вас бросать между ними.

Так вы можете сделать

match *self { 
    Some(ref val) => val as *const _ as *mut _, 
    None => ptr::null_mut(), 
} 

Читать неизменность ссылку, сделайте это непреложный указатель, а затем сделать его изменчивый указатель. Плюс все это делается через безопасный Rust, поэтому мы знаем, что мы не нарушаем никаких правил псевдонимов.

Это, скорее всего, очень плохая идея, чтобы на самом деле использовать указатель *mut до тех пор, пока ссылка &mut исчезнет. Я бы очень не решался с этим и попытался переосмыслить вашу упаковку на что-то более безопасное.