2016-06-24 4 views
0

Я вызываю функцию, которая принимает &[&str]. Как удобнее писать ["aa", "bb"], вместо &["aa", "bb"], я решил добавить AsRef:Как создать построитель, который принимает тип, который реализует AsRef для фрагмента `& str`?

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 
    } 

ответ

1

Код пытается передать право собственности на массив на функцию abc([]), возьмите ссылку на массив (s.as_ref()), затем он выбрасывает array, который оставил бы указатель на неопределенную память. Ржавчина не позволяет вам это делать.

Я сделал это: self.s = Some(unsafe { std::mem::transmute(s.as_ref()) });

Это очень плохая идея. Как уже упоминалось выше, теперь у вас есть ссылка на массив, который больше не существует. В этой памяти разрешено иметь что-нибудь, размещенное там в будущем, и доступ к указателю будет в случае наилучшим образом, что приведет к сбою вашей программы, но может также продолжаться, но с бессмысленными данными.

Используйте код unsafe, когда вы понимаете все последствия.

1

Обойти это сделать Builder быть родовым над S и быть владельцем параметра 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(); 
} 
+0

Как осущ 'AsRef 'для' str'? 'impl <'a, S: AsRef <[AsRef] >> Builder {' ​​не работает. – Lupe

+0

Я считал, что предлагаю что-то подобное, но поскольку '' 'завернут в' Option', похоже, что строитель должен быть вызван без 'abc'. Попробуйте это, и вы увидите, что тип не может быть выведен. Вы можете сделать значение по умолчанию 'S' или начать с пустого массива или чего-то еще. Если вы всегда * должны вызывать 'abc', то он должен быть предоставлен как часть конструктора строителя, и все это проще. – Shepmaster

+0

@ Lupe Можете ли вы пояснить, что вы хотите с примером? – malbarbo