1

I have code that transforms a string reference in some way, e.g. takes the first letter

trait Tr {
    fn trim_indent(self) -> Self;
}

impl<'a> Tr for &'a str {
    fn trim_indent(self) -> Self {
        &self[..1] // some transformation here
    }
}

fn main() {
    let s = "aaa".trim_indent();
    println!("{}", s);
}

Now I'm trying to generalize this code for any particular type that implements AsRef<str>. My final try was

use std::ops::Deref;

trait Tr<'a> {
    fn trim_indent(self) -> Deref<Target = str> + 'a + Sized;
}

impl<'a, T: AsRef<str>> Tr<'a> for T {
    fn trim_indent(self) -> Deref<Target = str> + 'a + Sized {
        self.as_ref()[..1] // some transformation here
    }
}

fn main() {
    let s = "aaa".trim_indent();
    println!("{}", s);
}

I'm stuck because without Sized I get an error that type is unknown at compile time, but with Size I get an error that I cannot use marker trait explicitly.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Alex Zhukovskiy
  • 9,565
  • 11
  • 75
  • 151
  • 1
    *e.g. takes the first letter* — that's not how Unicode works. See [Why is capitalizing the first letter of a string so convoluted in Rust?](https://stackoverflow.com/q/38406793/155423) for a discussion of how this is the wrong view to have. – Shepmaster May 22 '18 at 09:29
  • @Shepmaster okay, takes first byte and don't care what is it. Thank you for pointing that out, but it's just a badly written example. – Alex Zhukovskiy May 22 '18 at 09:50

1 Answers1

3

Regardless of what type you start with, the end type of slicing a &str is always a &str so your return type needs to be a &str.

Then it's a matter of implementing the trait for references to a type so that you can tie the input and output lifetimes together:

use std::rc::Rc;

trait Tr<'a> {
    fn trim_indent(self) -> &'a str;
}

impl<'a, T> Tr<'a> for &'a T
where
    T: AsRef<str> + 'a,
{
    fn trim_indent(self) -> &'a str {
        &self.as_ref()[..1] // Take the first **byte**
    }
}

fn main() {
    let s: &str = "aaa";
    println!("{}", s.trim_indent());

    let s: Box<str> = Box::from("bbb");
    println!("{}", s.trim_indent());

    let s: Rc<str> = Rc::from("ccc");
    println!("{}", s.trim_indent());
}

In this case, since all the types you've listed implement Deref anyway, you can just implement the trait for &str and all of the types can use it:

trait Tr {
    fn trim_indent(&self) -> &str;
}

impl Tr for str {
    fn trim_indent(&self) -> &str {
        &self[..1]
    }
}

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366