The Rust language provides excellent build-time embedding tools with the existing include_str! and include_bytes! macros, allowing us to embed static file content directly into the final binary. These are incredibly useful for configuration, static assets, or templates.
However, when working with Foreign Function Interfaces (FFI), especially those leveraging C libraries, we often encounter a common friction point: safely and efficiently embedding null-terminated C-style strings.
The Current Situation
-
include_str!: Returns&'static str(a UTF-8 slice). Converting this to a safe C string (e.g.,CStr) at runtime requires an allocation (viaCString::new) and checks for internal null bytes, which can panic. If the included file is known to be ASCII/valid C-string data, this conversion overhead is unnecessary. -
include_bytes!: Returns&'static [u8]. While this gives us the raw bytes, we still need to manually verify it is null-terminated and safely create a&'static core::ffi::CStrfrom it, often requiringunsafecode and careful handling of the final null byte.
The Case for include_c_str!
I propose adding a new macro, include_c_str!, that would work as follows:
- Signature: It would take a file path literal, similar to the existing macros.
// Example usage: let c_string: &'static core::ffi::CStr = include_c_str!("path/to/my_c_string.txt"); - Return Type: It would return a
&'static core::ffi::CStr. - Compile-Time Guarantee: The macro would perform compile-time validation on the file's contents to ensure:
- The file contains no internal null bytes (
\0) other than the optional, final terminator. - The file is properly null-terminated. If the file itself is not null-terminated, the macro should potentially append a null byte during embedding.
- If any of these conditions are violated, the compilation should fail with a descriptive error.
- The file contains no internal null bytes (
Benefits
- Safety & Ergonomics: Eliminates the need for manual
unsafecode to convert raw bytes or runtimeCStringallocation/error handling when dealing with embedded C-style strings. - Performance: Provides zero-cost abstraction for passing static C strings to FFI functions.
- Clarity: Clearly expresses the intent of embedding a file specifically as a static C string.
This addition would provide a complete set of static file inclusion tools for the most common embedded data types in Rust:
| Macro | Return Type | Use Case |
|---|---|---|
include_bytes! |
&'static [u8] |
Arbitrary binary data. |
include_str! |
&'static str |
UTF-8 text. |
include_c_str! |
&'static core::ffi::CStr |
Null-terminated C-style strings. |
What are your thoughts on this proposal? Would this improve your FFI workflow?