13

When looking at unix-socket, I came across this code:

let timeout = unsafe {
    let mut timeout: libc::timeval = mem::zeroed();
    let mut size = mem::size_of::<libc::timeval>() as libc::socklen_t;
    try!(cvt(libc::getsockopt(self.0,
                              libc::SOL_SOCKET,
                              kind,
                              &mut timeout as *mut _ as *mut _,
                              &mut size as *mut _ as *mut _)));
    timeout
};

I was curious about these lines in particular:

&mut timeout as *mut _ as *mut _,
&mut size as *mut _ as *mut _

Why is it necessary to perform two casts to a mutable raw pointer in a row? Why wouldn't it have been sufficient to only cast once?

Ryan C.
  • 87
  • 1
  • 6
DeBe
  • 491
  • 3
  • 15

1 Answers1

10

The timeout for example corresponds to a *mut c_void parameter:

pub unsafe extern fn getsockopt(sockfd: c_int, level: c_int, optname: c_int,
                                optval: *mut c_void, optlen: *mut socklen_t) -> c_int

The timeout in that file is defined as:

let mut timeout: libc::timeval = mem::zeroed();

So it's of type libc::timeval. Now let's consider:

&mut timeout as *mut _ as *mut _

First you have &mut timeout so that is of type &mut libc::timeval. Then you do as *mut _ to coerce it to a raw mutable pointer of an inferred type, which in this case is the same type of libc::timeval, so the full type so far is: *mut libc::timeval, which doesn't match the parameter type *mut c_void. The final as *mut _ again infers the target type, which is now the parameter type *mut c_void, so this finally coerces the *mut libc::timeval to a *mut c_void.

Jorge Israel Peña
  • 36,800
  • 16
  • 93
  • 123
  • Ah, I get it. Thanks for clarifying! Follow-up question: does rust infer the the second conversion because of the signature of `getsockopt` or is is just that *every* pointer gets cast to `*mut c_void` if I do the conversion often enough? Even for an arbitrary original type, let's say `&mut Vec`? – DeBe Jan 09 '16 at 08:37
  • Nah, thankfully not! That would be scary :S It's indeed because of the signature of `getsockopt`, specifically because the expression's position has that target type. The same would be the case e.g. if you were doing `let ptr: *mut c_void = &mut timeout as *mut _ as *mut _;`. – Jorge Israel Peña Jan 09 '16 at 08:38
  • This casting can be made a little shorter: `&mut timeout as *mut _ as _` (notice the last `mut` was removed. – rodrigocfd May 14 '21 at 13:14