10

I tried to derive serde::Deserialize for a struct containing a reference to a Path. This yielded an error message which doesn't occur if you replace &'a Path with &'a str. What causes the different behaviours of #[derive(Deserialize)]?

Playground

#!/bin/cargo script
//! ```cargo
//! [dependencies]
//! serde_derive="1.0"
//! serde="1.0"
//! ```

extern crate serde_derive;

use serde_derive::*;

#[derive(Deserialize)]
struct A<'a> {
    a: &'a std::path::Path,
    //a: &'a str,
}

fn main() {}
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'de` due to conflicting requirements
 --> src/main.rs:7:5
  |
7 |     a: &'a std::path::Path,
  |     ^
  |
note: first, the lifetime cannot outlive the lifetime 'de as defined on the impl at 5:10...
 --> src/main.rs:5:10
  |
5 | #[derive(Deserialize)]
  |          ^^^^^^^^^^^
  = note: ...so that the types are compatible:
          expected _IMPL_DESERIALIZE_FOR_A::_serde::de::SeqAccess<'_>
             found _IMPL_DESERIALIZE_FOR_A::_serde::de::SeqAccess<'de>
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 6:10...
 --> src/main.rs:6:10
  |
6 | struct A<'a> {
  |          ^^
  = note: ...so that the types are compatible:
          expected _IMPL_DESERIALIZE_FOR_A::_serde::Deserialize<'_>
             found _IMPL_DESERIALIZE_FOR_A::_serde::Deserialize<'_>

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'de` due to conflicting requirements
 --> src/main.rs:7:5
  |
7 |     a: &'a std::path::Path,
  |     ^
  |
note: first, the lifetime cannot outlive the lifetime 'de as defined on the impl at 5:10...
 --> src/main.rs:5:10
  |
5 | #[derive(Deserialize)]
  |          ^^^^^^^^^^^
  = note: ...so that the types are compatible:
          expected _IMPL_DESERIALIZE_FOR_A::_serde::de::MapAccess<'_>
             found _IMPL_DESERIALIZE_FOR_A::_serde::de::MapAccess<'de>
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 6:10...
 --> src/main.rs:6:10
  |
6 | struct A<'a> {
  |          ^^
  = note: ...so that the types are compatible:
          expected _IMPL_DESERIALIZE_FOR_A::_serde::Deserialize<'_>
             found _IMPL_DESERIALIZE_FOR_A::_serde::Deserialize<'_>

Strangely enough, the code compiles if the struct contains both fields _a: &'a Path and _b: &'a str... At this point I think this is a bug.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Long
  • 810
  • 4
  • 12
  • Use `a: PathBuf` – Boiethios May 31 '19 at 11:54
  • @FrenchBoiethios thanks for the recommendation, but I can afford to be stubborn on this one :) – Long May 31 '19 at 11:59
  • 1
    Hm, I looked at the `impl`s of `Deserialize` for `&'a str` and `&'a Path` and everything looks the same for both cases except for a type conversion via `AsRef`, but that's in the body of `PathVisitor` and that should long be typechecked when this error occurs. It looks like it might even be a bug in the lifetime inference. – Jan Hudec May 31 '19 at 12:25
  • @JanHudec thanks for investigating this. I filed a bug report, just in case. A faulty lifetime inference engine does sound nasty, so I hope it's not that ^^ – Long May 31 '19 at 13:00

1 Answers1

12

Add an attribute to the field: #[serde(borrow)]. That will indicate to serde that it should be borrowing the value. You must provide this attribute for every borrow except for &str and &[u8].

Source: https://serde.rs/lifetimes.html#borrowing-data-in-a-derived-impl

Marcus Griep
  • 8,156
  • 1
  • 23
  • 24