How to safely deal with C uint8_t* or char* in Rust

From your original post it would seem you are more concerned about safety than speed – rightfully. Did you measure that copying a flat byte buffer is indeed what slows your code down? memcpy() is incredibly fast these days.

Here it is:

use std::isize;
use std::slice;
use std::ops::{ Deref, DerefMut };

struct CBuffer {
    ptr: *mut u8,
    len: usize,
}

impl CBuffer {
    /// Constructor. Transfers ownership of `ptr`.
    pub unsafe fn from_owning(ptr: *mut u8, len: usize) -> Option<Self> {
        if ptr.is_null() || len > isize::MAX as usize {
            // slices are not allowed to be backed by a null pointer
            // or be longer than `isize::MAX`. Alignment is irrelevant for `u8`.
            None
        } else {
            Some(CBuffer { ptr, len })
        }
    }
}

impl Drop for CBuffer {
    /// Destructor.
    fn drop(&mut self) {
        unsafe {
            libc::free(self.ptr);
            // for example. Might need custom deallocation.
        }
    }
}

impl Deref for CBuffer {
    type Target = [u8];

    fn deref(&self) -> &Self::Target {
        unsafe {
            slice::from_raw_parts(self.buf as *const u8, self.len)
        }
    }
}

impl DerefMut for CBuffer {
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe {
            slice::from_raw_parts_mut(self.ptr, self.len)
        }
    }
}

A slice &[T] is explicitly convertible or implicitly coercible to a raw slice *const [T] which in turn is explicitly convertible to a pointer-to-first-element. There's also the convenience method <[T]>::as_ptr(). So you can easily pass a slice to a C function like this:

let slice: &[u8] = &[1, 2, 3, 4];
unsafe {
    c_function_taking_ptr_and_length(slice.as_ptr(), slice.len());
}

When you pass to a C function a slice pointing into memory that is owned by a Rust data structure, then you must not free it from C.

If you want to transfer ownership, you need to convert your data structure into a "forgetful" raw pointer (cf. Box::into_raw() or Vec::into_raw_parts()), use that in C, and when it's time to destroy the value, you need to pass it back to Rust, convert it back to an owning data type (cf. Box::from_raw() and Vec::from_raw_parts()), and let Rust free it. You can always write an extern "C" function in Rust for this purpose.