Hello,
Could you please tell me, is it possible to write a generic function that take OsStr(ing)
and Path(Buf)
and return OsStr
/Path
?
I'm trying to write strip_prefix
and split_once
that work some as the &str
functions. So far I was able to write strip_prefix
that works:
pub trait OsStrUtil {
fn osstr_strip_prefix(&self, prefix: &OsStr) -> Option<&OsStr>;
}
impl<Y> OsStrUtil for Y
where
Y: ?Sized + AsRef<OsStr>,
{
fn osstr_strip_prefix(&self, prefix: &OsStr) -> Option<&OsStr> {
self.as_ref()
.as_bytes()
.strip_prefix(prefix.as_bytes())
.map(|inp| OsStr::from_bytes(inp))
}
}
pub trait PathUtil {
fn path_strip_prefix(&self, prefix: &Path) -> Option<&Path>;
}
impl<Y> PathUtil for Y
where
Y: ?Sized + AsRef<Path>,
{
fn path_strip_prefix(&self, prefix: &Path) -> Option<&Path> {
self.as_ref()
.as_os_str()
.as_bytes()
.strip_prefix(prefix.as_os_str().as_bytes())
.map(|inp| Path::new(OsStr::from_bytes(inp)))
}
}
fn main() {
let x = OsStr::new("abcdef");
let x = x.osstr_strip_prefix(OsStr::new("abc"));
assert_eq!(x, Some(OsStr::new("def")));
let x = Path::new("abcdef");
let x = x.path_strip_prefix(Path::new("abc"));
assert_eq!(x, Some(Path::new("def")));
}
but I was unable to create a single generic function without compiler complaining about something.
Maybe that's not possible and that's OK too!
Thank you!
1 Like
How about this
pub fn strip_prefix<'a, S>(s: &'a S, prefix: &S) -> Option<&'a S>
where
S: AsRef<OsStr> + ?Sized,
OsStr: AsRef<S>,
{
s.as_ref()
.as_bytes()
.strip_prefix(prefix.as_ref().as_bytes())
.map(|inp| OsStr::from_bytes(inp).as_ref())
}
2 Likes
That's awesome @drewtato ! Thank you! I had no idea I can write OsStr: AsRef<S>
.
Do you know what's wrong with the trait implementation? For some reason I have problems to turn it into a trait...
pub trait OsStrUtil<S> {
fn strip_prefix<'a>(&'a self, prefix: &S) -> Option<&'a S>
where
Self: AsRef<OsStr>,
S: AsRef<OsStr>,
OsStr: AsRef<S>;
}
impl<S> OsStrUtil<S> for S {
fn strip_prefix<'a>(&'a self, prefix: &S) -> Option<&'a S>
where
Self: AsRef<OsStr>,
S: AsRef<OsStr>,
OsStr: AsRef<S>,
{
self.as_ref()
.as_bytes()
.strip_prefix(prefix.as_ref().as_bytes())
.map(|inp| OsStr::from_bytes(inp).as_ref())
}
}
fn main() {
let x = OsStr::new("abcdef");
let x = x.strip_prefix(OsStr::new("abc"));
assert_eq!(x, Some(OsStr::new("def")));
let x = Path::new("abcdef");
let x = x.strip_prefix(Path::new("abc"));
assert_eq!(x, Some(Path::new("def")));
}
I'd probably do it like this:
pub trait OsStrUtil: AsRef<OsStr>
where
OsStr: AsRef<Self>,
{
fn strip_prefix<'a>(&'a self, prefix: &Self) -> Option<&'a Self>;
}
impl<S> OsStrUtil for S
where
S: AsRef<OsStr> + ?Sized,
OsStr: AsRef<Self>,
{
fn strip_prefix<'a>(&'a self, prefix: &Self) -> Option<&'a Self> {
self.as_ref()
.as_bytes()
.strip_prefix(prefix.as_ref().as_bytes())
.map(|inp| OsStr::from_bytes(inp).as_ref())
}
}
Or if you want to be able to pass a different prefix type than Self
, you can put a generic on the function.
pub trait OsStrUtil: AsRef<OsStr>
where
OsStr: AsRef<Self>,
{
fn strip_prefix<'a, P: AsRef<OsStr> + ?Sized>(&'a self, prefix: &P) -> Option<&'a Self>;
}
impl<S> OsStrUtil for S
where
S: AsRef<OsStr> + ?Sized,
OsStr: AsRef<Self>,
{
fn strip_prefix<'a, P: AsRef<OsStr> + ?Sized>(&'a self, prefix: &P) -> Option<&'a Self> {
self.as_ref()
.as_bytes()
.strip_prefix(prefix.as_ref().as_bytes())
.map(|inp| OsStr::from_bytes(inp).as_ref())
}
}
1 Like
Thank you very much @drewtato . Unfortunately there is a problem converting into Path
fn main() {
let x = OsStr::new("abcdef");
let x = x.strip_prefix(OsStr::new("abc"));
assert_eq!(x, Some(OsStr::new("def")));
let x = Path::new("abcdef");
let x = x.strip_prefix(Path::new("abc"));
assert_eq!(x, Some(Path::new("def")));
// ^^^^^^^^^^^^^^^^^^^^^^ expected `Result<&Path, StripPrefixError>`, found `Option<&Path>`
}
That probably has something to do with more restrictions on Path...
That's because Path
has an inherent method with the same name but returns a different value. Changing the method name in your extension trait is the simplest solution.
1 Like
Yes, thank you very much folks!
pub trait OsStrUtil: AsRef<OsStr>
where
OsStr: AsRef<Self>,
{
fn strip_prefix_os<'a, P: AsRef<OsStr> + ?Sized>(&'a self, prefix: &P) -> Option<&'a Self>;
}
impl<S> OsStrUtil for S
where
S: AsRef<OsStr> + ?Sized,
OsStr: AsRef<Self>,
{
fn strip_prefix_os<'a, P: AsRef<OsStr> + ?Sized>(&'a self, prefix: &P) -> Option<&'a Self> {
self.as_ref()
.as_bytes()
.strip_prefix(prefix.as_ref().as_bytes())
.map(|inp| OsStr::from_bytes(inp).as_ref())
}
}
fn main() {
let x = OsStr::new("abcdef");
let x = x.strip_prefix_os(OsStr::new("abc"));
assert_eq!(x, Some(OsStr::new("def")));
let x = Path::new("abcdef");
let x = x.strip_prefix_os(Path::new("abc"));
assert_eq!(x, Some(Path::new("def")));
}
@parasyte , @drewtato do you think you could please help me with the second function? I thought split_once
will be easy after strip_prefix
but apparently not
use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
pub trait OsStrUtil: AsRef<OsStr>
where
OsStr: AsRef<Self>,
{
fn split_once_os<'a, P: AsRef<OsStr> + ?Sized>(&'a self, separator: &P) -> Option<(&'a Self, &'a Self)>;
}
impl<S> OsStrUtil for S
where
S: AsRef<OsStr> + ?Sized,
OsStr: AsRef<Self>,
{
fn split_once_os<'a, P: AsRef<OsStr> + ?Sized>(&'a self, separator: &P) -> Option<(&'a Self, &'a Self)> {
self.as_ref()
.as_bytes()
//////////////
////.split_once(separator.as_ref().as_bytes())
/// use of unstable library feature 'slice_split_once': newly added
//////////////
.map(|(left, right)| (OsStr::from_bytes(left).as_ref(), OsStr::from_bytes(right).as_ref()))
}
}
fn main() {
let x = OsStr::new("abcdef");
let x = x.split_once_os(OsStr::new("cd"));
assert_eq!(x, Some((OsStr::new("ab"), OsStr::new("ef"))));
let x = Path::new("abcdef");
let x = x.split_once_os(Path::new("cd"));
assert_eq!(x, Some((Path::new("ab"), Path::new("ef"))));
}
split_once
doesn't even do what you want it to, it takes a single element while you want to split on a string. You just have to do it yourself.
fn split_once_os<'a, P: AsRef<OsStr> + ?Sized>(
&'a self,
separator: &P,
) -> Option<(&'a Self, &'a Self)> {
let slice = self.as_ref().as_bytes();
let separator = separator.as_ref().as_bytes();
let index = std::iter::successors(Some(slice), |slice| slice.get(1..))
.map_while(|slice| slice.get(..separator.len()))
.position(|slice| slice == separator)?;
let (left, rest) = slice.split_at(index);
let right = &rest[separator.len()..];
Some((
OsStr::from_bytes(left).as_ref(),
OsStr::from_bytes(right).as_ref(),
))
}
1 Like
That's fantastic, thank you.