Hacker News new | ask | show | jobs
by fredmorcos 3738 days ago
Yes! That definitely reinforces my understanding. So Rust is more like C and less like C++ in that it is pass-by-value everywhere, with the exception that references are not pointers but _real_ references.

Does this make things like "const int * const x" (in C syntax) pointless in Rust?

Does that mean that in Rust I can only pass to a function an immutable reference to a mutable object, but not a mutable reference to an immutable object?

Also, out of curiosity, how would something like a recv() call into the middle of an array (buffer) look like in rust? Something like "recv(sock_fd, buf + 5 * sizeof(char), buf_len - 5 * sizeof(char), 0)"?

1 comments

Great :)

  > Does this make things like "const int * const x"
  > (in C syntax) pointless in Rust?
Checking myself with cdecl, because I _always_ get this wrong:

  > declare x as const pointer to const int
This is

  let x: &i32;
(using i32 because it's the default int type, even though it's different than C's int.)

The variations are:

  let x: &i32; // an immutable binding to an immutable reference
  let mut x: &i32; // a mutable binding to an immutable reference
  let x: &mut i32; // an immutable binding to a mutable reference
  let mut x: &mut i32; // a mutable binding to a mutable reference
More mutablity == longer declaration, roughly.

  > Does that mean that in Rust I can only pass to a function an immutable reference
  > to a mutable object,but not a mutable reference to an immutable object?
You're right in both. No problem treating something mutable as immutable, but treat something immutable as mutable and you get a compiler error.

  > Also, out of curiosity,
Rust has a concept called "slices". These are "fat pointers", that have both a pointer and a length inside:

  let x = vec![1, 2, 3, 4, 5];
  let slice = &x[1..3];
Here, 'slice' will be a pair, (ptr, len), so the ptr will point to the interior of the vector, and the length will be 2. If we printed it, we'd see "2, 3".

So let's check out the signature of recv:

  ssize_t recv(int sockfd, void *buf, size_t len, int flags);
This pointer, length pair looks suspiciously like buf and len here. That's for good reason. A first step towards a more Rust-like wrapper over recv would look like this:

  fn recv(socket: libc::c_int, buf: &mut [u8], flags: libc::c_int) -> libc::ssize_t {
    let ptr = buf.as_mut_ptr() as *mut c_void;
    let len = buf.len() as libc::size_t;

    unsafe { recv(socket, ptr, len, flags) }
  }
which would end up being called like this:

  recv(sock_fd, slice, 0);
combining the names from your example and mine from the slice above, heh.

The next step would be to turn `flags` into an enum, and take that as an argument rather than a C int. Then you'd want to convert the return type to a Rust integer rather than a C one... eventually, you end up with the API we have in the standard library, which looks like this for a udp socket, for example:

  use std::net::UdpSocket;

  {
      let mut socket = try!(UdpSocket::bind("127.0.0.1:34254"));

      // read ten bytes from the socket
      let mut buf = [0; 10];
      let (amt, src) = try!(socket.recv_from(&mut buf));

  } // the socket is closed here
A few comments on this:

We only said &mut buf, but I showed you syntax with [] above. When you pass a reference to an array or vector, and the function is expecting a slice, it will automatically convert to a slice of the full length. To be more accurate to your original question:

      let (amt, src) = try!(socket.recv_from(&mut buf[1, 5]));
or whatever middle part of the buffer you want.

amt is the amount of bytes read, and the src is the address, in this particular API.

You'll notice this particular API doesn't expose flags: we try to Do The Right Thing in the standard library, so you don't worry about these. Here's the source of rec_from: https://github.com/rust-lang/rust/blob/master/src/libstd/sys... c::recvfrom is libc::recvfrom, rather than recv, technically. But as an example from a different API, when opening files, we set O_CLOEXEC where appropriate: https://github.com/rust-lang/rust/pull/27971

If you want the more exotic options, then you have to dig in and make the calls yourself. Such is the pain of a standard library, trying to make the common, good case good, but we still let you dig in and build a different abstraction if you don't like ours.