rustfficompile-time-constantnull-terminated

include_str for null terminated string


I need to read a file into a null terminated string at compile time.

Working in Rust OpenGL. I have a shader source code stored in a separate file. The function that will eventually read the source is gl::ShaderSource from the gl crate. All it needs is a pointer to a null terminated string (the std::ffi::CStr type).

Typically the guides I have seen read the shader source file using include_str!, then at run time allocate a whole new buffer of length +1, then copy the original source into the new buffer and put the terminating 0 at the end. I'd like to avoid all that redundant allocating and copying and just have the correctly null terminated string at compile time.

I realize it is somewhat petty to want to avoid an extra allocation for a short shader file, but the principle could apply to many other types of larger constants.


While scrolling through suggested questions during the preview I saw this: How do I expose a compile time generated static C string through FFI?

which led me to this solution:

    let bytes1 = concat!(include_str!("triangle.vertex_shader"), "\0");
    let bytes2 = bytes1.as_bytes();
    let bytes3 = unsafe {
        CStr::from_bytes_with_nul_unchecked(bytes2)
    };
    println!("{:?}", bytes3);

Does this accomplish avoiding the runtime allocation and copying?


Solution

  • Yes that should work as intended. If you want you can even bundle it into a simple macro.

    macro_rules! include_cstr {
        ($file:expr) => {{
            // Create as explicit constant to force from_bytes_with_nul_unchecked to
            // perform compile time saftey checks.
            const CSTR: &'static ::std::ffi::CStr = unsafe {
                let input = concat!($file, "\0");
                ::std::ffi::CStr::from_bytes_with_nul_unchecked(input.as_bytes())
            };
            
            CSTR
        }};
    }
    
    const VERTEX_SHADER: &'static CStr = include_cstr!("shaders/vert.glsl");
    const FRAGMENT_SHADER: &'static CStr = include_cstr!("shaders/frag.glsl");