Skip to main content

mlx_c_rust/types/
string.rs

1//! Owned MLX string handle with the crate's captured-error semantics.
2
3use std::ffi::{CStr, CString};
4
5use crate::MlxCError;
6use crate::error::check_status;
7use crate::raw;
8
9/// Owned MLX string handle released exactly once through the official C API.
10#[derive(Debug)]
11pub struct MlxString(raw::mlx_string);
12
13impl MlxString {
14    /// Creates an empty string handle for an output parameter.
15    pub fn empty() -> Self {
16        // SAFETY: The runtime error handler is installed before model loading,
17        // and the returned handle enters RAII ownership immediately.
18        let raw_string = unsafe { raw::mlx_string_new() };
19        Self(raw_string)
20    }
21
22    /// Copies the source string handle into this handle.
23    pub fn set(&mut self, source: &Self) -> Result<(), MlxCError> {
24        // SAFETY: Both handles are live and this call only replaces the owned
25        // string value.
26        let status = unsafe { raw::mlx_string_set(&mut self.0, source.0) };
27        check_status(status, "copy an MLX string")
28    }
29
30    /// Copies the owned string text into Rust memory.
31    #[must_use]
32    pub fn to_string_lossy(&self) -> String {
33        // SAFETY: `self` owns a live string handle whose data pointer stays
34        // valid for the duration of this call.
35        let data_pointer = unsafe { raw::mlx_string_data(self.0) };
36        if data_pointer.is_null() {
37            return String::new();
38        }
39        // SAFETY: MLX C documents a null-terminated string owned by the
40        // handle; the text is copied before the borrow ends.
41        unsafe { CStr::from_ptr(data_pointer) }
42            .to_string_lossy()
43            .into_owned()
44    }
45
46    pub(crate) fn raw_mut(&mut self) -> *mut raw::mlx_string {
47        &mut self.0
48    }
49
50    pub(crate) fn require_populated(&self, operation: &'static str) -> Result<(), MlxCError> {
51        if self.0.ctx.is_null() {
52            return Err(MlxCError {
53                operation,
54                description: "MLX returned an empty string handle".to_owned(),
55            });
56        }
57        Ok(())
58    }
59}
60
61impl Drop for MlxString {
62    fn drop(&mut self) {
63        // SAFETY: This owner releases its live string exactly once and never
64        // accesses the handle afterward.
65        unsafe {
66            raw::mlx_string_free(self.0);
67        }
68    }
69}
70
71/// Creates an owned string from host UTF-8 text, so `"text".parse::<MlxString>()`
72/// and `MlxString::from_str` both construct live handles.
73impl std::str::FromStr for MlxString {
74    type Err = MlxCError;
75
76    fn from_str(value: &str) -> Result<Self, Self::Err> {
77        let value_argument = CString::new(value).map_err(|_| MlxCError {
78            operation: "create an MLX string",
79            description: "string contains an interior null byte".to_owned(),
80        })?;
81        // SAFETY: The C string remains valid for this copying constructor and
82        // the returned handle enters RAII ownership.
83        let raw_string = unsafe { raw::mlx_string_new_data(value_argument.as_ptr()) };
84        let string = Self(raw_string);
85        string.require_populated("create an MLX string")?;
86        Ok(string)
87    }
88}