Unfortunately, this is non-trivial. The safest way by far is to cheat a bit and swap out the PathBuf
's buffer until you're done with it (Rust Playground):
use std::{
mem, ops,
path::{Path, PathBuf},
};
pub fn get_mut(path_buf: &mut PathBuf) -> impl ops::Deref<Target = Path> + ops::DerefMut + '_ {
struct PathMut<'a>(&'a mut PathBuf, Box<Path>);
impl Drop for PathMut<'_> {
fn drop(&mut self) {
let path = mem::replace(&mut self.1, PathBuf::new().into_boxed_path());
*self.0 = path.into_path_buf();
}
}
impl ops::Deref for PathMut<'_> {
type Target = Path;
fn deref(&self) -> &Self::Target {
&self.1
}
}
impl ops::DerefMut for PathMut<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.1
}
}
let path = mem::take(path_buf).into_boxed_path();
PathMut(path_buf, path)
}
A dangerous way (that many will frown upon) is to take the documentation's word for it that PathBuf
is a thin wrapper over OsString
(Rust Playground):
use std::{
ffi::{OsStr, OsString},
mem,
path::{Path, PathBuf},
};
pub fn get_mut(path_buf: &mut PathBuf) -> &mut Path {
// sanity check
const _: () = assert!(mem::size_of::<PathBuf>() == mem::size_of::<OsString>());
// SAFETY: `PathBuf` contains an `OsString`, and it is not large enough to
// contain any fields before it.
let os_string = unsafe { &mut *(path_buf as *mut PathBuf as *mut OsString) };
let os_str = &mut **os_string;
// SAFETY: The existence of `AsRef<OsStr> for str` and `AsRef<Path> for str`
// implies that `OsStr` and `Path` can store arbitrary code units of size 1 and
// alignment 1. Unless the language is changed to add more kinds of DSTs, this
// means that `OsStr` and `Path` are both slice DSTs with size-1 alignment-1
// elements. Therefore, their DST metadata are compatible, and they can be
// soundly converted via a pointer cast.
let path = unsafe { &mut *(os_str as *mut OsStr as *mut Path) };
path
}
A somewhat less dangerous (but still very sketchy) way is to rely on PathBuf
and OsString
not to assert unique ownership over their internal buffers (Rust Playground):
use std::{
ffi::OsStr,
mem,
path::{Path, PathBuf},
};
pub fn get_mut(path_buf: &mut PathBuf) -> &mut Path {
let mut os_string = mem::take(path_buf).into_os_string();
let os_str = &mut *os_string;
// SAFETY: As before. Also, neither `PathBuf` nor `OsString` asserts unique
// ownership over its internal buffer, so the reference is not invalidated when
// we move the `OsString` back.
let path = unsafe { &mut *(os_str as *mut OsStr as *mut Path) };
*path_buf = os_string.into();
path
}
The question is, what do you want to do with a &mut Path
in the first place? The type has no special APIs: everything that can be done with a &mut Path
(without making unsafe layout assumptions) can also be done with a &Path
. That's why there aren't any &mut OsStr -> &mut Path
APIs or similar, since the latter type is basically useless.