0
votes

I am calling a function that takes a &[&str]. As it is more convenient to write ["aa", "bb"], instead of &["aa", "bb"], I decided to add AsRef:

struct Builder<'a> {
    s: Option<&'a [&'a str]>,
}

impl<'a> Builder<'a> {
    fn new() -> Builder<'a> {
        Builder { s: None }
    }

    fn abc<S>(&mut self, s: S) -> &mut Self
        where S: AsRef<[&'a str]> + 'a
    {
        self.s = Some(s.as_ref());
        self
    }

    fn build(&self) -> u32 {
        0
    }
}

fn main() {
    Builder::new().abc([]).build();
}

(Playground)

But there is a problem with lifetime:

error: `s` does not live long enough
        self.s = Some(s.as_ref());
                      ^
note: reference must be valid for the lifetime 'a as defined on the block at 12:4...
    {
        self.s = Some(s.as_ref());
        self
    }
note: ...but borrowed value is only valid for the scope of parameters for function at 12:4
    {
        self.s = Some(s.as_ref());
        self
    }
2

2 Answers

1
votes

The code tries to transfer ownership of the array to the function abc([]), take a reference to the array (s.as_ref()), then it throws away the array, which would leave a pointer to undefined memory. Rust does not allow you to do that.

I did it: self.s = Some(unsafe { std::mem::transmute(s.as_ref()) });

This is a very bad idea. As mentioned above, you now have a reference to an array that no longer exists. That memory is allowed to have anything placed there in the future, and accessing the pointer will, in the best case, cause your program to crash, but could also continue executing but with nonsense data.

Only use unsafe code when you understand all the consequences.

1
votes

A workaround is to make Builder be generic over S and be the owner of parameter s:

struct Builder<S> {
    s: Option<S>,
}

impl<'a> Builder<[&'a str; 0]> {
    fn new() -> Self {
        Builder { s: None }
    }
}

impl<'a, S: AsRef<[&'a str]>> Builder<S> {
    fn abc<T: AsRef<[&'a str]>>(self, s: T) -> Builder<T> {
        Builder {
            // copy other fields, x: self.x, ...
            // and replace s
            s: Some(s)
        }
    }

    fn print_s(&self) {
        // example of using s
        if let Some(ref s) = self.s {
            println!("{:?}", s.as_ref()); // S::as_ref
        } else {
            println!("None");
        }
    }
}

Now abc can be called with different parameter types:

fn main() {
    Builder::new().print_s();
    Builder::new().abc([]).print_s();
    Builder::new().abc(["a", "b"]).print_s();
    Builder::new().abc(&["a", "b", "c"]).print_s();
    Builder::new().abc(vec!["a"]).print_s();
}