如何创建一个构建器,该构建器采用一种为`& str`片实现AsRef的类型?

时间:2016-06-24 14:12:06

标签: rust lifetime

我正在调用一个&[&str]的函数。由于编写["aa", "bb"]而不是&["aa", "bb"]更方便,我决定添加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

但是生命存在问题:

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 个答案:

答案 0 :(得分:1)

代码尝试将数组的所有权转移到函数abc([]),引用数组(s.as_ref()),然后抛弃数组,会留下指向未定义内存的指针。 Rust不允许你这样做。

  

我做到了:self.s = Some(unsafe { std::mem::transmute(s.as_ref()) });

这是非常糟糕的主意。如上所述,您现在可以引用不再存在的数组。该内存将来可以放置任何,并且在最佳情况下访问指针会导致程序崩溃,但也可能继续执行但是无意义的数据。

只有在了解所有后果后才能使用unsafe代码。

答案 1 :(得分:1)

解决方法是让BuilderS上具有通用性并成为参数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");
        }
    }
}

现在可以使用不同的参数类型调用abc

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();
}