Convert Option<&mut T> to *mut T

Viewed 1880

I'm writing a Rust wrapper around a C library and while doing so I'm trying to take advantage of the "nullable pointer optimization" mentioned in The Book, but I can't find a good way to convert Option<&T> to *const T and Option<&mut T> to *mut T like what they're describing.

What I really want is to be able to call Some(&foo) as *const _. Unfortunately that doesn't work, so the next best thing I can think of is a trait on Option<T> that enables me to call Some(&foo).as_ptr(). The following code is a working definition and implementation for that trait:

use std::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(),
        }
    }
}

Now that I can call Some(&foo).as_ptr() to get a *const _, I want to be able to call Some(&mut foo).as_ptr() to get a *mut _. The following is the new trait I made to do this:

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(),
        }
    }
}

The problem is, the AsMutPtr trait won't compile. When I try, I get the following error:

error[E0507]: cannot move out of borrowed content
  --> src/lib.rs:22:15
   |
22 |         match *self {
   |               ^^^^^
   |               |
   |               cannot move out of borrowed content
   |               help: consider removing the `*`: `self`
23 |             Some(val) => val as *mut _,
   |                  --- data moved here
   |
note: move occurs because `val` has type `&mut T`, which does not implement the `Copy` trait
  --> src/lib.rs:23:18
   |
23 |             Some(val) => val as *mut _,
   |                  ^^^

I don't see what changed between the two traits that causes it to fail — I didn't think adding mut would make that big a difference. I tried adding a ref, but that just causes a different error, and I wouldn't expect to need that anyway.

Why doesn't the AsMutPtr trait work?

4 Answers

To avoid unsafe code, change the trait to accept &mut self instead of either self or &self:

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

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

You could also reduce the implementation to one line, if you felt like it:

fn as_mut_ptr(&mut self) -> *mut T {
    self.as_mut().map_or_else(ptr::null_mut, |v| *v)
}

This can be used to give you multiple mutable raw pointers from the same source. This can easily lead you to causing mutable aliasing, so be careful:

fn example(mut v: Option<&mut u8>) {
    let b = v.as_mut_ptr();
    let a = v.as_mut_ptr();
}

I would recommend not converting the immutable reference to a mutable pointer as this is very likely to cause undefined behavior.

Related