2017-10-01 29 views
10

Trong một dự án có các phương thức serialization và deserialization Serde (1.0) tùy chỉnh, tôi đã dựa vào thói quen kiểm tra này để kiểm tra xem serializing một đối tượng và ngược lại có mang lại một đối tượng tương đương hay không.Làm thế nào chúng ta có thể viết một chức năng chung để kiểm tra serde serialization và deserialization?

// let o: T = ...; 
let buf: Vec<u8> = to_vec(&o).unwrap(); 
let o2: T = from_slice(&buf).unwrap(); 
assert_eq!(o, o2); 

Làm nội tuyến này hoạt động khá tốt. Bước tiếp theo của tôi hướng tới khả năng sử dụng lại là tạo một hàm check_serde cho mục đích này.

pub fn check_serde<T>(o: T) 
where 
    T: Debug + PartialEq<T> + Serialize + DeserializeOwned, 
{ 
    let buf: Vec<u8> = to_vec(&o).unwrap(); 
    let o2: T = from_slice(&buf).unwrap(); 
    assert_eq!(o, o2); 
} 

này hoạt động tốt cho việc sở hữu các loại, nhưng không phải với nhiều loại với giới hạn tuổi thọ (Playground):

check_serde(5); 
check_serde(vec![1, 2, 5]); 
check_serde("five".to_string()); 
check_serde("wait"); // [E0279] 

Lỗi:

error[E0279]: the requirement `for<'de> 'de : ` is not satisfied (`expected bound lifetime parameter 'de, found concrete lifetime`) 
    --> src/main.rs:24:5 
    | 
24 |  check_serde("wait"); // [E0277] 
    |  ^^^^^^^^^^^ 
    | 
    = note: required because of the requirements on the impl of `for<'de> serde::Deserialize<'de>` for `&str` 
    = note: required because of the requirements on the impl of `serde::de::DeserializeOwned` for `&str` 
    = note: required by `check_serde` 

Như tôi muốn thực hiện các công việc chức năng với những trường hợp này (bao gồm các cấu trúc có lát cắt chuỗi), tôi đã thử một phiên bản mới với thời gian tồn tại của đối tượng deserialization:

pub fn check_serde<'a, T>(o: &'a T) 
where 
    T: Debug + PartialEq<T> + Serialize + Deserialize<'a>, 
{ 
    let buf: Vec<u8> = to_vec(o).unwrap(); 
    let o2: T = from_slice(&buf).unwrap(); 
    assert_eq!(o, &o2); 
} 

check_serde(&5); 
check_serde(&vec![1, 2, 5]); 
check_serde(&"five".to_string()); 
check_serde(&"wait"); // [E0405] 

Triển khai này dẫn đến một vấn đề khác và nó sẽ không biên dịch (Playground).

error[E0597]: `buf` does not live long enough 
    --> src/main.rs:14:29 
    | 
14 |  let o2: T = from_slice(&buf).unwrap(); 
    |        ^^^ does not live long enough 
15 |  assert_eq!(o, &o2); 
16 | } 
    | - borrowed value only lives until here 
    | 
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 10:1... 
    --> src/main.rs:10:1 
    | 
10 |/pub fn check_serde<'a, T>(o: &'a T) 
11 | |  where T: Debug + PartialEq<T> + Serialize + Deserialize<'a> 
12 | | { 
13 | |  let buf: Vec<u8> = to_vec(o).unwrap(); 
14 | |  let o2: T = from_slice(&buf).unwrap(); 
15 | |  assert_eq!(o, &o2); 
16 | | } 
    | |_^ 

Tôi đã mong đợi này: phiên bản này ngụ ý rằng các nội dung theo bộ (và do đó các đối tượng deserialized) sống miễn là đối tượng đầu vào, đó là không đúng sự thật. Bộ đệm chỉ có nghĩa là sống miễn là phạm vi của hàm.

Nỗ lực thứ ba của tôi tìm cách xây dựng các phiên bản thuộc sở hữu của đầu vào gốc, do đó tránh được vấn đề có đối tượng được deserialized với các giới hạn tuổi thọ khác nhau. Các đặc tính ToOwned xuất hiện cho phù hợp với trường hợp sử dụng này.

pub fn check_serde<'a, T: ?Sized>(o: &'a T) 
where 
    T: Debug + ToOwned + PartialEq<<T as ToOwned>::Owned> + Serialize, 
    <T as ToOwned>::Owned: Debug + DeserializeOwned, 
{ 
    let buf: Vec<u8> = to_vec(&o).unwrap(); 
    let o2: T::Owned = from_slice(&buf).unwrap(); 
    assert_eq!(o, &o2); 
} 

Điều này làm cho công việc chức năng cho đồng bằng chuỗi lát bây giờ, nhưng không cho các đối tượng hỗn chứa chúng (Playground):

check_serde(&5); 
check_serde(&vec![1, 2, 5]); 
check_serde(&"five".to_string()); 
check_serde("wait"); 
check_serde(&("There's more!", 36)); // [E0279] 

Một lần nữa, chúng ta vấp ngã khi loại lỗi tương tự như phiên bản đầu tiên:

error[E0279]: the requirement `for<'de> 'de : ` is not satisfied (`expected bound lifetime parameter 'de, found concrete lifetime`) 
    --> src/main.rs:25:5 
    | 
25 |  check_serde(&("There's more!", 36)); // [E0279] 
    |  ^^^^^^^^^^^ 
    | 
    = note: required because of the requirements on the impl of `for<'de> serde::Deserialize<'de>` for `&str` 
    = note: required because of the requirements on the impl of `for<'de> serde::Deserialize<'de>` for `(&str, {integer})` 
    = note: required because of the requirements on the impl of `serde::de::DeserializeOwned` for `(&str, {integer})` 
    = note: required by `check_serde` 

Được cấp, tôi đang thua lỗ. Làm thế nào chúng ta có thể xây dựng một chức năng chung chung, bằng cách sử dụng Serde, serializes một đối tượng và deserializes nó trở lại vào một đối tượng mới? Đặc biệt, chức năng này có thể được thực hiện trong Rust (ổn định hoặc hàng đêm), và nếu có, những điều chỉnh nào để triển khai của tôi bị thiếu?

Trả lời

5

Thật không may, những gì bạn cần là một tính năng tha t chưa được triển khai trong Rust: các loại liên quan chung.

Hãy nhìn vào một biến thể khác nhau của check_serde:

pub fn check_serde<T>(o: T) 
where 
    for<'a> T: Debug + PartialEq<T> + Serialize + Deserialize<'a>, 
{ 
    let buf: Vec<u8> = to_vec(&o).unwrap(); 
    let o2: T = from_slice(&buf).unwrap(); 
    assert_eq!(o, o2); 
} 

fn main() { 
    check_serde("wait"); // [E0279] 
} 

Vấn đề ở đây là o2 không thể loại T: o2 đề cập đến buf, mà là một biến địa phương, nhưng thông số loại không thể được suy ra các loại bị hạn chế bởi tuổi thọ được giới hạn trong phần thân của hàm. Chúng tôi muốn cho T là một cái gì đó như &strmà không cần một thời gian cụ thể gắn liền với nó.

Với loại liên quan đến chung chung, điều này có thể được giải quyết với một cái gì đó như thế này (rõ ràng là tôi không thể kiểm tra nó, vì nó chưa được thực hiện):

trait SerdeFamily { 
    type Member<'a>: Debug + PartialEq<Self> + Serialize + Deserialize<'a>; 
} 

struct I32Family; 
struct StrFamily; 

impl SerdeFamily for I32Family { 
    type Member<'a> = i32; // we can ignore parameters 
} 

impl SerdeFamily for StrFamily { 
    type Member<'a> = &'a str; 
} 

pub fn check_serde<'a, Family>(o: Family::Member<'a>) 
where 
    Family: SerdeFamily, 
{ 
    let buf: Vec<u8> = to_vec(&o).unwrap(); 
    // `o2` is of type `Family::Member<'b>` 
    // with a lifetime 'b different from 'a 
    let o2: Family::Member = from_slice(&buf).unwrap(); 
    assert_eq!(o, o2); 
} 

fn main() { 
    check_serde::<I32Family>(5); 
    check_serde::<StrFamily>("wait"); 
} 
3

answer from Francis Gagné đã cho thấy rằng chúng tôi không thể thực hiện điều này một cách hiệu quả nếu không có các loại liên quan chung. Việc thiết lập quyền sở hữu sâu sắc đối tượng deserialized là một công việc có thể xung quanh mà tôi mô tả ở đây.

Nỗ lực thứ ba rất gần với giải pháp linh hoạt, nhưng nó thiếu ngắn do cách hoạt động của std::borrow::ToOwned. Đặc điểm này không phù hợp để lấy ra một phiên bản sở hữu sâu sắc của một đối tượng. Ví dụ, việc cố gắng sử dụng tính năng Thực thi ToOwned cho &str sẽ cung cấp cho bạn một lát chuỗi khác.

let a: &str = "hello"; 
let b: String = (&a).to_owned(); // expected String, got &str 

Tương tự như vậy, các loại Owned cho một struct chứa chuỗi lát không thể là một struct chứa String s. Trong mã:

#[derive(Debug, PartialEq, Serialize, Deserialize)] 
struct Foo<'a>(&str, i32); 

#[derive(Debug, PartialEq, Serialize, Deserialize)] 
struct FooOwned(String, i32); 

Chúng ta không thể impl ToOwned cho Foo để cung cấp FooOwned vì:

  • Nếu chúng ta lấy được Clone, việc thực hiện ToOwned cho T: Clone chỉ áp dụng cho Owned = Self.
  • Ngay cả khi triển khai tùy chỉnh ToOwned, đặc điểm này yêu cầu loại sở hữu có thể được vay thành loại ban đầu (do ràng buộc Owned: Borrow<Self>). Đó là, chúng ta có nghĩa vụ có thể lấy ra một số &Foo(&str, i32) trong số FooOwned, nhưng cấu trúc bên trong của chúng thì khác nhau, và vì vậy điều này không thể đạt được.

Điều này có nghĩa là, để thực hiện theo phương pháp thứ ba, chúng tôi cần một đặc điểm khác. Hãy có một đặc điểm mới ToDeeplyOwned biến một đối tượng thành một đối tượng hoàn toàn thuộc sở hữu, không có lát hoặc tham chiếu liên quan.

pub trait ToDeeplyOwned { 
    type Owned; 
    fn to_deeply_owned(&self) -> Self::Owned; 
} 

Mục đích ở đây là tạo bản sao sâu ra bất kỳ thứ gì. Có vẻ như không dễ dàng thực hiện tất cả, nhưng một số thủ thuật là có thể. Trước tiên, chúng tôi có thể triển khai nó cho tất cả các loại tham chiếu trong đó T: ToDeeplyOwned.

impl<'a, T: ?Sized + ToDeeplyOwned> ToDeeplyOwned for &'a T { 
    type Owned = T::Owned; 
    fn to_deeply_owned(&self) -> Self::Owned { 
     (**self).to_deeply_owned() 
    } 
} 

Tại thời điểm này, chúng tôi sẽ phải triển khai có chọn lọc cho các loại không tham chiếu mà chúng tôi biết điều đó là ổn. Tôi đã viết một macro để làm cho quá trình này ít tiết, sử dụng to_owned() nội bộ.

macro_rules! impl_deeply_owned { 
    ($t: ty, $t2: ty) => { // turn $t into $t2 
     impl ToDeeplyOwned for $t { 
      type Owned = $t2; 
      fn to_deeply_owned(&self) -> Self::Owned { 
       self.to_owned() 
      } 
     } 
    }; 
    ($t: ty) => { // turn $t into itself, self-contained type 
     impl ToDeeplyOwned for $t { 
      type Owned = $t; 
      fn to_deeply_owned(&self) -> Self::Owned { 
       self.to_owned() 
      } 
     } 
    }; 
} 

Đối với các ví dụ trong câu hỏi để làm việc, chúng ta cần ít nhất sau đây:

impl_deeply_owned!(i32); 
impl_deeply_owned!(String); 
impl_deeply_owned!(Vec<i32>); 
impl_deeply_owned!(str, String); 

Một khi chúng ta thực hiện các đặc điểm cần thiết về Foo/FooOwned và thích ứng serde_check sử dụng đặc điểm mới, mã bây giờ biên dịch và chạy thành công (Playground):

#[derive(Debug, PartialEq, Serialize)] 
struct Foo<'a>(&'a str, i32); 

#[derive(Debug, PartialEq, Clone, Deserialize)] 
struct FooOwned(String, i32); 

impl<'a> ToDeeplyOwned for Foo<'a> { 
    type Owned = FooOwned; 

    fn to_deeply_owned(&self) -> FooOwned { 
     FooOwned(self.0.to_string(), self.1) 
    } 
} 

impl<'a> PartialEq<FooOwned> for Foo<'a> { 
    fn eq(&self, o: &FooOwned) -> bool { 
     self.0 == o.0 && self.1 == o.1 
    } 
} 

pub fn check_serde<'a, T: ?Sized>(o: &'a T) 
where 
    T: Debug + ToDeeplyOwned + PartialEq<<T as ToDeeplyOwned>::Owned> + Serialize, 
    <T as ToDeeplyOwned>::Owned: Debug + DeserializeOwned, 
{ 
    let buf: Vec<u8> = to_vec(&o).unwrap(); 
    let o2: T::Owned = from_slice(&buf).unwrap(); 
    assert_eq!(o, &o2); 
} 

// all of these are ok 
check_serde(&5); 
check_serde(&vec![1, 2, 5]); 
check_serde(&"five".to_string()); 
check_serde("wait"); 
check_serde(&"wait"); 
check_serde(&Foo("There's more!", 36)); 
Các vấn đề liên quan