2017-10-01 3 views
10

사용자 정의 Serde (1.0) 직렬화 및 직렬화 해제 메소드가 관련된 프로젝트에서이 테스트 루틴을 사용하여 객체와 직렬을 직렬화하면 동일한 객체가 생성되는지 여부를 확인했습니다.Serde 직렬화와 직렬화를 검사하기위한 일반 함수를 작성하려면 어떻게해야합니까?

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

이 인라인을 사용하면 효과가 있습니다. 재사용을 향한 나의 다음 단계는이 목적을 위해 기능을 check_serde으로 만드는 것이었다.

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

이 평생의 경계를 가지는 유형에 대한 유형을 소유 잘 작동하지만,하지 (Playground) :

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

오류 : 나는 함수 작업을하고자으로

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` 

이러한 경우 (문자열 슬라이스가있는 구조체 포함) 명시 적 객체 직렬화 수명이있는 새 버전을 시도했습니다.

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] 

이 구현으로 인해 다른 문제가 발생하며 컴파일되지 않습니다 (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 | | } 
    | |_^ 

는 나는 이미이 일 것으로 예상 한 :이 버전은 직렬화 된 내용이 (그래서 직렬화 복원 오브젝트가) 사실이 아니다 입력 개체로 오래 사는 것을 의미한다. 버퍼는 함수의 범위만큼 살아 있어야합니다.

제 3의 시도는 원래 입력의 소유 버전을 빌드하여 수명 기간 경계가 서로 다른 직렬화되지 않은 객체를 갖는 문제를 피하기위한 것입니다. ToOwned 특성은이 사용 사례에 적합한 것으로 보입니다. 우리는 첫 번째 버전과 같은 오류 종류에 우연히 발견,

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

을 다시 :

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

이 지금은 일반 문자열 조각에 대한 기능을 작동 할 수있게하지만, 복합 개체에 대해 (Playground)을 포함하지 않는 :

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` 

허락합니다. 어떻게 Serde를 사용하여 객체를 직렬화하고 새로운 객체로 역 직렬화하는 제네릭 함수를 만들 수 있습니까? 특히이 기능을 녹 (안정 또는 야간)에서 만들 수 있습니까? 그렇다면 구현에 대한 조정 사항이 누락 되었습니까?

답변

5

, 당신이 필요로하는 기능 그쪽은 t는 Rust : generic 관련 유형에서 아직 구현되지 않았습니다.

의이 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] 
} 

여기서 문제는 o2 유형 T이 될 수 없다는 것입니다 : o2는 지역 변수 인 buf을 의미하지만, 입력 매개 변수를 추정 할 수 없다 함수의 본문에만 제한된 수명으로 제약되는 유형 T&str과 같아야하며 특정 평생 시간이 첨부되어 있지 않아야합니다.

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é은 일반적인 관련 유형이 없으면 효율적으로 처리 할 수 ​​없음을 보여줍니다. deserialize 된 객체의 깊은 소유권을 설정하는 것은 여기에서 설명 할 수있는 가능한 해결 방법입니다.

세 번째 시도는 유연한 솔루션에 매우 가깝지만, std::borrow::ToOwned의 작동 방식으로 인해 부족합니다. 이 특성은 객체의 깊이 소유 된 버전을 검색하는 데 적합하지 않습니다. 예를 들어 &str에 대한 ToOwned 구현을 사용하려고하면 다른 문자열 슬라이스가 생깁니다.

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

마찬가지로 구조체에 포함 된 문자열에 대한 Owned 슬라이스 타입 String S를 포함하는 구조체가 될 수 없다. 우리가 Clone을 유도하는 경우, T: Clone에 대한 ToOwned의 구현 Owned = Self에만 적용

  • : 코드에서 : FooOwned 때문에

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

    우리가 제공하는 Foo에 대한 ToOwned을 IMPL 수 없습니다.

  • 사용자 지정 구현이 ToOwned 인 경우에도 소유 형식을 원래 형식으로 빌릴 수 있어야합니다 (제한 조건 Owned: Borrow<Self>). 즉, FooOwned에서 &Foo(&str, i32)을 검색 할 수 있지만 내부 구조가 다르기 때문에이를 달성 할 수 없습니다.

이것은 세 번째 방법을 따르기 위해 다른 특성이 필요하다는 것을 의미합니다. ToDeeplyOwned 개체를 완전히 소유 한 조각으로 바꾸거나 슬라이스 나 참조가 필요없는 새로운 특성을 갖도록합시다.

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

여기 목적은 무엇에서 깊은 사본을 생산하는 것입니다. catch-all 구현이 쉬운 것은 아니지만 몇 가지 트릭이 가능합니다. 먼저 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() 
    } 
} 

이 시점에서 우리는 그것이 알 수있는 비 참조 유형에 선택적으로 구현해야합니다. to_owned()을 내부적으로 사용하는이 프로세스를 덜 장황하게 만들기위한 매크로를 작성했습니다.

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

우리가 Foo/FooOwned에 필요한 특성을 구현하고 새로운 특성을 사용하는 serde_check을 적용하면, 코드 : 질문의 예를 들어

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

우리는 적어도이 필요 작동합니다 ( 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)); 
관련 문제