1

I have the following code that works, together with 3 comments that show what struct+impl I'd like to have instead - EventJSON:

pub fn gen(&self, ith: i64) -> Out {

    //let mut ev = EventJSON::new(self.num_features);
    let mut serializer = serde_json::ser::Serializer::new(Vec::new());
    let mut map = serializer.serialize_map(Some(self.num_features)).unwrap();
    
    for f in 0..self.num_features {
        let name = self.feature_names.get(f).unwrap();

        //ev.insert(name, &ith.to_string());
        map.serialize_entry(name, &ith.to_string())
            .expect("Can't serialize feature {}.");            
    }
    map.end().expect("Serialization failed.");

    //ev.serialize()
    String::from_utf8(serializer.into_inner()).expect("Invalid UTF-8.")
}    

However, if I try to put such code inside a struct and corresponding impl block I get errors with lifetimes & borrowing in the new() method:

struct EventJSON {
    serializer: Option<serde_json::ser::Serializer<Vec<u8>>>,
    // Should be a SerializeMap, but that's not exposed in the API, using this type given by VS Code:
    map: Option<serde_json::ser::Compound<'static, Vec<u8>, serde_json::ser::CompactFormatter>>,  
}

impl EventJSON {

    // Can't figure out how to make this compile!
    // I need to have a struct with the map and serializer, for subsequent
    // insert() calls, and finally, serialize().
    pub fn new(size: usize) -> EventJSON {

        let mut serializer = serde_json::ser::Serializer::new(Vec::new());
        // This borrows, and at the same time says serializer doesn't live long enough!?
        let mut map = serializer.serialize_map(Some(size)).unwrap();
        
        EventJSON {
            serializer: Some(serializer), // But I place serializer at the output. Why doesn't it live enough?!
            map: Some(map),
        }
    }

    // No compiler errors here.
    pub fn insert(&mut self, k: &str, v: &str) {
        if let Some(map) = &mut self.map {
            map.serialize_entry(k, v);
        } else {
            panic!("Already serialized! Unusable object!");
        }
    }

    // And no compiler errors here.
    pub fn serialize(&mut self) -> String {
        if let Some(serializer) = self.serializer.take() {
            String::from_utf8(serializer.into_inner()).expect("Invalid UTF-8.")
        } else {
            panic!("Already serialized! Unusable object!");
        }
    }
}

however I get these errors:

error[E0597]: `serializer` does not live long enough
  --> src/lib.rs:17:23
   |
17 |         let mut map = serializer.serialize_map(Some(size)).unwrap();
   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |                       |
   |                       borrowed value does not live long enough
   |                       argument requires that `serializer` is borrowed for `'static`
...
23 |     }
   |     - `serializer` dropped here while still borrowed

error[E0505]: cannot move out of `serializer` because it is borrowed
  --> src/lib.rs:20:30
   |
17 |         let mut map = serializer.serialize_map(Some(size)).unwrap();
   |                       ------------------------------------
   |                       |
   |                       borrow of `serializer` occurs here
   |                       argument requires that `serializer` is borrowed for `'static`
...
20 |             serializer: Some(serializer),
   |                              ^^^^^^^^^^ move out of `serializer` occurs here

how can one go around the borrow-checker to make that EventJSON::new() work? I spent a lot of time and am clueless.

Here's the full code if it helps learn-rust/rest-injector @ github. If you do cargo run you should get these same errors.

Alberto
  • 565
  • 5
  • 14
  • 1
    [Don't post text as images](https://meta.stackoverflow.com/questions/285551/why-should-i-not-upload-images-of-code-data-errors-when-asking-a-question). – Chayim Friedman Apr 13 '22 at 22:25
  • 1
    The type created by `serializer.serialize_map()` contains a reference to the original `serializer`. Constructing `EventJSON` with `serializer` will *move* it and invalidate references to it, leaving `map` in an invalid state. Your field of `Compound<'static, ...` is why the error says it should be borrowed for `'static`, but trying to change it to express what you want isn't possible anyway. You're trying to create a *self-referential* struct which is problematic: [Why can't I store a value and a reference to that value in the same struct?](https://stackoverflow.com/q/32300132/2189130). – kmdreko Apr 14 '22 at 00:18
  • But it's not impossible right? I read that question & answers and people can use owning_ref and others? (I tried using it and met other errors though) Otherwise, how can one build efficient abstractions in Rust without leaking the intermediate resources and increasing the amount of things the callee has to manage? This question is a simple case like that which I assume comes up often in Rust? – Alberto Apr 15 '22 at 14:53

0 Answers0