Some API methods require exclusive (&mut self) access even though the underlying SDL functions do not require mutable access to the Rust wrapper. These methods only forward the underlying SDL handle to an FFI function and do not mutate any Rust-visible state. This leads to unnecessary borrow checker errors during normal usage (e.g. acquiring a swapchain texture and continuing to record commands, or updating a window title during rendering) which are otherwise valid SDL usage patterns. Changing these methods to accept &self removes these artificial restrictions without reducing safety and making the API more ergonomic.
Some affected methods:
CommandBuffer::wait_and_acquire_swapchain_texture
Current API:
pub fn wait_and_acquire_swapchain_texture<'a>(
&'a mut self,
window: &Window,
) -> Result<Texture<'a>, Error>
Why the &mut self is unnecessarily restrictive here:
The function simply calls:
SDL_WaitAndAcquireGPUSwapchainTexture(...)
and returns a Texture<'a> whose lifetime is already tied to the CommandBuffer. The Texture<'a> lifetime ensures that the swapchain texture cannot outlive the command buffer. Requiring &mut self does not provide additional safety. It only creates borrow checker conflicts. The returned Texture<'a> already carries a lifetime tied to the borrowed CommandBuffer. This prevents the swapchain texture from outliving the command buffer. Changing the receiver from &mut self to &self does not weaken this guarantee; it only removes the unnecessary requirement for exclusive access to the wrapper object.
Example
The following is a perfectly valid SDL GPU workflow:
let swapchain = cmd.wait_and_acquire_swapchain_texture(&window)?;
// Record commands
cmd.push_vertex_uniform_data(...);
let render_pass = device.begin_render_pass(...)?;
...
device.end_render_pass(render_pass);
// Blit into the swapchain
cmd.blit_texture(...);
cmd.submit()?;
This workflow is directly based on SDL's official TriangleMSAA.c example ([https://github.com/TheSpydog/SDL_gpu_examples/blob/main/Examples/TriangleMSAA.c]). The current Rust signature prevents expressing this valid SDL usage pattern in safe Rust without introducing unnecessary workarounds.
With the current signature, the mutable borrow used to obtain the swapchain prevents later calls on cmd, even though SDL expects this usage pattern.
Changing the receiver to &self eliminates the artificial borrow conflict while the returned Texture<'a> still guarantees correct lifetime semantics.
CommandBuffer::acquire_swapchain_texture
Current API
pub fn acquire_swapchain_texture<'a>(
&'a mut self,
window: &Window,
) -> Result<Option<Texture<'a>>, Error>
This function also only forwards the command buffer handle to SDL and returns a texture tied to the command buffer lifetime. Changing the receiver to &self maintains the same lifetime guarantees while avoiding unnecessary exclusive borrows.
Window::set_title
Current API
pub fn set_title(&mut self, title: &str) -> Result<(), NulError>
Underlying SDL API
bool SDL_SetWindowTitle(SDL_Window *window, const char *title);
The implementation simply converts the Rust string into a CString and calls the SDL function:
let title = CString::new(title)?;
unsafe {
SDL_SetWindowTitle(self.context.raw, title.as_ptr());
}
The rust window wrapper object itself is not mutated; the method only forwards the underlying SDL_Window* to SDL.
Example
let swapchain = cmd.wait_and_acquire_swapchain_texture(&window)?;
...
window.set_title(&format!("Frames per second: {:.2}", fps))?;
With the current API, this fails because set_title requires a mutable borrow while window is already immutably borrowed elsewhere.
Changing the signature to:
pub fn set_title(&self, title: &str)
removes the borrow conflict without affecting safety.
As a workaround to change the window title during runtime in my application, I had to call unsafe functions from the sdl3-sys crate:
pub fn set_window_title(sdl_window: &sdl3::video::Window, title: &str) {
let title = CString::new(title).unwrap();
unsafe {
sys::video::SDL_SetWindowTitle(sdl_window.raw(), title.as_ptr() as *const c_char);
}
}
After i made a local copy of the sdl3-rs crate and changed the api accordingly i was able to to use the safe Rust API instead. I tested all of the proposed changes locally by modifying the receiver types in my copy of the crate. The examples compiled successfully, including a complete MSAA rendering path ported from SDL's C examples, and no additional borrow checker issues were introduced.
Taken together:
All three methods only forward the underlying SDL handle to the corresponding SDL function and do not mutate any Rust-visible state within the wrapper. Changing the receiver from &mut self to &self preserves the existing lifetime and safety guarantees while eliminating unnecessary borrow checker conflicts. It also allows valid SDL usage patterns from the C API and official examples to be expressed naturally in safe Rust.
Some API methods require exclusive (
&mut self) access even though the underlying SDL functions do not require mutable access to the Rust wrapper. These methods only forward the underlying SDL handle to an FFI function and do not mutate any Rust-visible state. This leads to unnecessary borrow checker errors during normal usage (e.g. acquiring a swapchain texture and continuing to record commands, or updating a window title during rendering) which are otherwise valid SDL usage patterns. Changing these methods to accept &self removes these artificial restrictions without reducing safety and making the API more ergonomic.Some affected methods:
CommandBuffer::wait_and_acquire_swapchain_textureCurrent API:
Why the &mut self is unnecessarily restrictive here:
The function simply calls:
SDL_WaitAndAcquireGPUSwapchainTexture(...)and returns a
Texture<'a>whose lifetime is already tied to the CommandBuffer. TheTexture<'a>lifetime ensures that the swapchain texture cannot outlive the command buffer. Requiring&mut selfdoes not provide additional safety. It only creates borrow checker conflicts. The returned Texture<'a> already carries a lifetime tied to the borrowed CommandBuffer. This prevents the swapchain texture from outliving the command buffer. Changing the receiver from &mut self to &self does not weaken this guarantee; it only removes the unnecessary requirement for exclusive access to the wrapper object.Example
The following is a perfectly valid SDL GPU workflow:
This workflow is directly based on SDL's official TriangleMSAA.c example ([https://github.com/TheSpydog/SDL_gpu_examples/blob/main/Examples/TriangleMSAA.c]). The current Rust signature prevents expressing this valid SDL usage pattern in safe Rust without introducing unnecessary workarounds.
With the current signature, the mutable borrow used to obtain the swapchain prevents later calls on cmd, even though SDL expects this usage pattern.
Changing the receiver to
&selfeliminates the artificial borrow conflict while the returnedTexture<'a>still guarantees correct lifetime semantics.CommandBuffer::acquire_swapchain_textureCurrent API
This function also only forwards the command buffer handle to SDL and returns a texture tied to the command buffer lifetime. Changing the receiver to
&selfmaintains the same lifetime guarantees while avoiding unnecessary exclusive borrows.Window::set_titleCurrent API
pub fn set_title(&mut self, title: &str) -> Result<(), NulError>Underlying SDL API
bool SDL_SetWindowTitle(SDL_Window *window, const char *title);The implementation simply converts the Rust string into a CString and calls the SDL function:
The rust window wrapper object itself is not mutated; the method only forwards the underlying SDL_Window* to SDL.
Example
With the current API, this fails because set_title requires a mutable borrow while window is already immutably borrowed elsewhere.
Changing the signature to:
pub fn set_title(&self, title: &str)removes the borrow conflict without affecting safety.
As a workaround to change the window title during runtime in my application, I had to call unsafe functions from the sdl3-sys crate:
After i made a local copy of the sdl3-rs crate and changed the api accordingly i was able to to use the safe Rust API instead. I tested all of the proposed changes locally by modifying the receiver types in my copy of the crate. The examples compiled successfully, including a complete MSAA rendering path ported from SDL's C examples, and no additional borrow checker issues were introduced.
Taken together:
All three methods only forward the underlying SDL handle to the corresponding SDL function and do not mutate any Rust-visible state within the wrapper. Changing the receiver from &mut self to &self preserves the existing lifetime and safety guarantees while eliminating unnecessary borrow checker conflicts. It also allows valid SDL usage patterns from the C API and official examples to be expressed naturally in safe Rust.