6

I have read How to read a struct from file in Rust?, but std::slice::raw::mut_buf_as_slice has been deprecated, hence I want to re-ask this question.

I want to read a struct utmp from /var/run/utmp, and I have tried the following code:

fn read_utmp() -> Utmp {
    let mut reader = BufReader::new(File::open("/var/run/utmp").unwrap());
    let mut ut = vec![];
    let size = mem::size_of::<Utmp>();
    reader.take(size as u64).read_to_end(&mut ut);
    unsafe {
        std::mem::transmute(ut)
    }
}

And as expected, the compiler complaint:

error: transmute called with differently sized types: std::vec::Vec (192 bits) to Utmp (3056 bits) [E0512]

How could I do this?

Community
  • 1
  • 1
knight42
  • 910
  • 10
  • 15
  • The question you referenced has now been updated. – Shepmaster Jul 12 '16 at 19:08
  • *but `std::slice::raw::mut_buf_as_slice` has been deprecated, hence I want to re-ask this question* — in the future, it may be more productive to ask what the replacement for the deprecated functionality is. – Shepmaster Jul 12 '16 at 19:10

1 Answers1

5

I believe that raw::mut_buf_as_slice was replaced with slice::from_raw_parts_mut.


Note that the following code does not take into account any endianness or padding issues and is intended to be used with POD types. struct utmp should be safe in this case.


Here is a function that can read a struct (of a pod type) from a file:

use std::io::{self, BufReader, Read};
use std::fs::{self, File};
use std::path::Path;
use std::slice;

fn read_struct<T, R: Read>(mut read: R) -> io::Result<T> {
    let num_bytes = ::std::mem::size_of::<T>();
    unsafe {
        let mut s = ::std::mem::uninitialized();
        let mut buffer = slice::from_raw_parts_mut(&mut s as *mut T as *mut u8, num_bytes);
        match read.read_exact(buffer) {
            Ok(()) => Ok(s),
            Err(e) => {
                ::std::mem::forget(s);
                Err(e)
            }
        }
    }
}

// use
// read_struct::<Utmp>(reader)

If you want to read all utmp structs from the utmp file, you can execute read_struct multiple times or read all the file at once:

fn read_structs<T, P: AsRef<Path>>(path: P) -> io::Result<Vec<T>> {
    let path = path.as_ref();
    let struct_size = ::std::mem::size_of::<T>();
    let num_bytes = try!(fs::metadata(path)).len() as usize;
    let num_structs = num_bytes / struct_size;
    let mut reader = BufReader::new(try!(File::open(path)));
    let mut r = Vec::<T>::with_capacity(num_structs);
    unsafe {
        let mut buffer = slice::from_raw_parts_mut(r.as_mut_ptr() as *mut u8, num_bytes);
        try!(reader.read_exact(buffer));
        r.set_len(num_structs);
    }
    Ok(r)
}

// use
// read_structs::<Utmp, _>("/var/run/utmp"))
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
malbarbo
  • 10,717
  • 1
  • 42
  • 57