`serde::from_value` moves my value but I want I returned to me on Err

I'm trying to avoid cloning up front in the code below as in most cases the serde::from_value will succeed. In the case where Foo is a large object, I don't want to pay the clone cost just because every now and then the deserialization fails and I want to save the original json to permanent storage.

If serde_from_value moves the json value I would like it back if deserialization fails. I don't see how this is possible. Does anyone have any strategies for avoiding a clone in the case where I have to accept a serde_json::Value as an arg to convert_to_foo?

#[derive(Debug, serde::Deserialize)]
struct Foo {
    name: String

fn convert_to_foo(json: serde_json::Value) {
    match serde_json::from_value(json) {
                               // ^^^^ I could clone here 
        Ok(foo) => save_foo_to_db(foo),
        Err(_) => save_failure_to_db(json)
                                  // ^^^^ value used here after move

fn save_foo_to_db(_foo: Foo) {

fn save_failure_to_db(_json: serde_json::Value) {

fn main() {
    let json = serde_json::json!({ "name": "Zenora" });

Here you go:

1 Like

Ugggg that is shamefully simple :laughing:
Thanks Alice

Yes, you can avoid the clone. serde_json::from_value just calls the Deserializer implementation of Value internally; but there's also an implementation for &Value. They're slightly different; the owned implementation can do things such as passing Strings by-value to the Visitor, so for String fields, there'd be no cloning of those Strings with the owned Deserializer, while the strings would need to be clones when just using &Value as a deserializer. But since parts of the Value struct would be passed into the Deserialize implementation of your struct anyways, when you use the owned deserializer, there's no way of getting the Value back on error, even in principle.

As for how this looks in code, @alice already showed how it's done while I was writing this answer.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.