I tried to implement AsRef and ToOwned traits for two structs that hold OsStr/OsString but I can't figure out how to configure lifetimes for it. Could you please tell me is it possible to impl these traits?
this definition is not compatible with the ToOwned and AsRef traits.
for example, the return type of Borrow<Borrowed>::borrow() is &Borrowed, where the elided lifetime is derived from &self, but your type contains a lifetime which is unrelated to &self, so it's not possible to implement Borrow<MyString<'_>> for any type.
if you can make the the wrapper type of OsStr unsized, then it's trivial to implement these, something like:
struct MyString {
// other fields must come before the unsized `OsStr`
data: OsStr,
}
You can not do that. You can only ever return a reference to something that exists somewhere else, never to something you just created[1].
ToOwned should work as-is.
For the others, you want methods that return an ownedMyOsStr<'a> from a &'a OsStr.
The only exception to this if your type looks exactly the same in memory as something that already exists, in which case you can pointer cast your reference to a reference of the other type using unsafe. This is what AsRef<Path> for OsStr does. But that does not seem to apply here, because your type has additional fields. ↩︎
On top of what has already been said, you should know about the orphan rules regarding trait implementations. What you attempted to do that cannot be done since you are trying to implement a trait not from your crate for a type not from your crate.
The implementation of the AsRef trait should look like this:
The orphan rules care about generic impls mainly, so if you have a non-generic impl with an uncovered local type as implementor or a trait parameter, you're good.[1]
overlap check might bite, depending on upstream's generic impls ↩︎
This has the benefit of making your API more flexible:
// Now my_function doesn't need to know about MyString in its signature
fn my_function(s: &str) -> &str {
MyString::from(s).other // do something with MyString
}
By ordering I mean that the implementing type T0 is always the first position considered, and then T1, T2, and so on down the trait parameters. (When I say things like "come first" below, I'm using this ordering.)
Also, the covered parameters (parameters of other types, .. above) don't matter (keeping in mind that &Tn, Box<Tn>, and so on don't cover Tn due to some type constructors being fundamental).[1]
Alternatively phrased: at least one of T0, T1, T2, or T3 must be a local type [constructor], where Box<Tn> etc are considered local. That's enough for the non-generic case.
// "`T1`" (`LocalType`) is local so this is fine
impl ForeignTrait<LocalType> for ForeignType
// There's only "`T0`" and it's `ForeignType<..>` so this is not fine
impl ForeignTrait for ForeignType<LocalType>
For the generic case: nothing before the first local can be an uncovered type parameter. But covered type parameters are fine.
// The `T` is covered so this is fine
impl<T> From<LocalType> for Vec<T> { /* ... */ }
// The `LocalType` comes first so this is fine
impl<T: AsRef<str>> AsRef<T> for LocalType { /* ... */ }
// The `T` is uncovered and comes before `LocalType` so this is not fine
impl<T: AsRef<str>> AsRef<LocalType> for T { /* ... */ }
The covered parameters don't matter to the orphan rules. They can matter for overlap checks. ↩︎
That's the ordering which I guess I wasn't clear enough about. The implementing type is always "first", and then the trait parameters, from left to right. I'll edit my most.