I'm using this C code to encode several values into a byte string:
unsigned char *buf = malloc(10);
*(unsigned int *)buf = 6;
memcpy(buf + 4, source, 6);
// Repeat for more values...
It copies a uint32
with the length of the value followed by a byte string consisting of the value.
How can this be done in Rust? There doesn't seem to be a straightforward way (that I know of) to manipulate memory at arbitrary alignments and widths.
user4815162342's answer provides a direct translation. Here is a more idiomatic Rust approach:
use byteorder::{BigEndian, WriteBytesExt}; // 1.3.4
use std::io::Write;
fn main() {
let source = [0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let mut buff = Vec::with_capacity(10);
buff.write_i32::<BigEndian>(6).unwrap();
buff.write(&source).unwrap();
}
This works because the Write
trait is implemented for Vec<u8>
.
Using the byteorder
crate allows you to specify the endianness you want the length to be written with.
There are some differences to the C version:
write
would expand the size of the buffer as needed (although it will be more efficient if you create it with the needed size to begin with)
write
returns a Result
indicating if there was an error. In this sample I chose to unwrap the result, which would panic if there was an error. However as pointed out by @shepmaster, the current implementation of write
on Vec
only ever returns an Ok
variant, so you could reduce the overhead of checking the result by discarding it like this:
let _ = buff.write(&source);
Your example is extremely low-level; it would help if you told us what high-level goal you want achieved, and we might be able to recommend an elegant way to do it in Rust.
However, Rust is a systems language, so it's definitely possible to translate your code into Rust, just take into account that it: a) won't be safe, and b) won't be elegant. By not safe I mean you'll literally need to use unsafe
to allow unguarded writing to pointers, and figuratively that a bug might easily cause undefined behavior. And it's not elegant because there are usually nicer ways to achieve the same functionality.
With that said, here is a direct translation of the C code:
use std::alloc::{self, Layout};
use std::{mem, ptr};
fn main() {
let source = [0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9];
unsafe {
let layout = Layout::from_size_align(10, mem::align_of::<i32>()).unwrap();
let buf = alloc::alloc(layout); // buf = malloc(10)
*(buf as *mut i32) = 6; // *(unsigned int *)buf = 6;
ptr::copy(source.as_ptr(), buf, 6); // memcpy(buf, source, 6);
// fill the rest of buf with something, otherwise UB on read
// use buf...
alloc::dealloc(buf, layout); // free(buf)
}
}
This kind of code is almost never normally written in Rust programs. It is used in special situations, such as when implementing a crate that offers a safe abstraction not yet covered by existing crates, and reviewed with utmost care. For example, the code written in this answer originally contained a non-trivial bug kindly pointed out by a commenter (now fixed). Also, the code doesn't check for allocation failure (but neither did the original C code in the question).
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.