Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deserializing to std::borrow::Cow<str> always allocates #1852

Closed
Vlad-Shcherbina opened this issue Nov 22, 2019 · 10 comments
Closed

Deserializing to std::borrow::Cow<str> always allocates #1852

Vlad-Shcherbina opened this issue Nov 22, 2019 · 10 comments

Comments

@Vlad-Shcherbina
Copy link

// [dependencies]
// serde_json = "1.0.41"
fn main() {
    let v = "hello";
    let s = serde_json::to_string(&v).unwrap();
    println!("{}", s);
    let v2: std::borrow::Cow<str> = serde_json::from_str(&s).unwrap();
    match v2 {
        std::borrow::Cow::Borrowed(..) => println!("borrowed"),
        std::borrow::Cow::Owned(..) => println!("owned"),
    }
}

Expected result

"hello"
borrowed

Because string hello appears in the data as is, with no escaping or reader buffer boundaries.

Actual result

"hello"
owned
@Vlad-Shcherbina
Copy link
Author

This happens because serde has a blanket impl for Cow that doesn't care about Cow::Borrowed:

serde/serde/src/de/impls.rs

Lines 1726 to 1739 in d540e72

#[cfg(any(feature = "std", feature = "alloc"))]
impl<'de, 'a, T: ?Sized> Deserialize<'de> for Cow<'a, T>
where
T: ToOwned,
T::Owned: Deserialize<'de>,
{
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
T::Owned::deserialize(deserializer).map(Cow::Owned)
}
}

If specialization was in stable it would be possible to create an impl for Cow<str> that understands borrowed strs.

@cormacrelf
Copy link

You can work around this with deserialize_with.

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=418dd6b98dfa62d43c4cc7fa8b7ea0d6

See (and run) the test for usage.

@pickfire
Copy link

pickfire commented Feb 12, 2020

Would adding specialization for this helps? Such as for T: Sized.

@dtolnay dtolnay transferred this issue from serde-rs/json Jul 5, 2020
@chpio
Copy link

chpio commented Aug 24, 2021

Seem to work ok by putting the Cow in a newtype wrapper and adding the borrow attribute to the Cow field: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9ba7a398755c35aa9ad24baa7c745371

@mqudsi
Copy link

mqudsi commented Nov 18, 2022

Is the demonstration from above the only way to use deserialize_with to support both borrowed and owned deserialization?

I realized that I can ship a library w/ its standardized json format and struct definitions with #[deserialize_with = ...] annotated everywhere correctly, include tests that cover json deserialization using serde_json::from_str() that pass perfectly, then realize later that a simple deserialize_with() target that doesn't delegate work to a Visitor impl but rather just deserializes on the spot can end up failing every time if a consumer of the library tries to deserialize to the same struct using serde_json::from_reader() instead of serde_json::from_string().

@Lucretiel
Copy link

Lucretiel commented Nov 18, 2022

Why would it fail? from_reader would use visit_str instead of visit_borrowed_str, so it should still work (you'd just get all owned strings)

@mqudsi
Copy link

mqudsi commented Nov 18, 2022

That's only if you specifically implement and go through the Deserialize/Visitor trait. As I mentioned, I was asking about the simpler/less boilerplate self-contained approach of attempting to decode directly in the function named as the deserialize_with target.

@dtolnay
Copy link
Member

dtolnay commented Jul 9, 2023

I think this is the intended behavior. For better or worse, there only gets to be 1 Deserialize<'de> trait impl for Cow<'a, str>. Either that impl has a 'de: 'a bound, or it doesn't, and different common usage patterns need a deserialize_with and/or borrow attribute either way. I think the way this is currently implemented is the best tradeoff.

@dtolnay dtolnay closed this as completed Jul 9, 2023
@fogti
Copy link

fogti commented Jul 10, 2023

Can the other alternative be implemented, perhaps via a wrapper? has someone done so already? (similar to the serde-bytes stuff?; perhaps even put it into exactly that crate?)

@dtolnay
Copy link
Member

dtolnay commented Jul 10, 2023

Yeah, if it would be helpful someone could implement BorrowCow that mirrors the API of Cow but with the opposite lifetime bound for the Deserialize impl.

jszwec added a commit to jszwec/serde_json_borrow that referenced this issue Jan 19, 2025
serde by always creates Cow::Owned even if it's possible to Borrow:
serde-rs/serde#1852 (comment)

We create our own wrapper around Cow in order to allow Borrowed keys.

This significantly improves performance:
parse
simple_json
serde_json                               Avg: 283.92 MB/s (-0.84%)     Median: 284.16 MB/s (-0.26%)     [279.00 MB/s .. 289.83 MB/s]
serde_json + access by key               Avg: 276.34 MB/s (-1.24%)     Median: 276.46 MB/s (-1.11%)     [267.63 MB/s .. 284.46 MB/s]
serde_json_borrow                        Avg: 534.96 MB/s (+19.37%)    Median: 533.52 MB/s (+18.98%)    [517.16 MB/s .. 561.15 MB/s]
serde_json_borrow + access by key        Avg: 530.26 MB/s (+18.52%)    Median: 529.16 MB/s (+18.28%)    [519.93 MB/s .. 544.36 MB/s]
SIMD_json_borrow                         Avg: 227.52 MB/s (-0.40%)     Median: 229.19 MB/s (+0.40%)     [184.71 MB/s .. 237.90 MB/s]
hdfs
serde_json                               Avg: 573.25 MB/s (-1.02%)     Median: 573.38 MB/s (-0.78%)     [549.59 MB/s .. 587.74 MB/s]
serde_json + access by key               Avg: 610.33 MB/s (-0.88%)     Median: 609.03 MB/s (-1.00%)     [587.33 MB/s .. 631.20 MB/s]
serde_json_borrow                        Avg: 1.0051 GB/s (+19.36%)    Median: 1.0037 GB/s (+18.06%)    [978.71 MB/s .. 1.0418 GB/s]
serde_json_borrow + access by key        Avg: 1.0453 GB/s (+20.75%)    Median: 1.0432 GB/s (+20.01%)    [1.0123 GB/s .. 1.0802 GB/s]
SIMD_json_borrow                         Avg: 551.42 MB/s (+0.26%)     Median: 554.60 MB/s (+0.91%)     [478.56 MB/s .. 566.50 MB/s]
hdfs_with_array
serde_json                               Avg: 470.84 MB/s (-0.24%)    Median: 470.70 MB/s (-0.20%)    [460.56 MB/s .. 480.44 MB/s]
serde_json + access by key               Avg: 471.89 MB/s (+0.09%)    Median: 471.92 MB/s (-0.03%)    [459.45 MB/s .. 484.84 MB/s]
serde_json_borrow                        Avg: 813.40 MB/s (+7.66%)    Median: 816.02 MB/s (+7.78%)    [798.52 MB/s .. 834.84 MB/s]
serde_json_borrow + access by key        Avg: 821.93 MB/s (+7.64%)    Median: 822.22 MB/s (+7.67%)    [786.16 MB/s .. 838.18 MB/s]
SIMD_json_borrow                         Avg: 458.26 MB/s (-0.15%)    Median: 457.11 MB/s (-0.39%)    [445.08 MB/s .. 473.40 MB/s]
wiki
serde_json                               Avg: 1.2361 GB/s (-0.93%)    Median: 1.2367 GB/s (-1.24%)    [1.2083 GB/s .. 1.2777 GB/s]
serde_json + access by key               Avg: 1.2717 GB/s (-0.63%)    Median: 1.2715 GB/s (-1.91%)    [1.1773 GB/s .. 1.3294 GB/s]
serde_json_borrow                        Avg: 1.4936 GB/s (+3.09%)    Median: 1.4950 GB/s (+3.02%)    [1.4339 GB/s .. 1.5327 GB/s]
serde_json_borrow + access by key        Avg: 1.5255 GB/s (+3.51%)    Median: 1.5266 GB/s (+3.13%)    [1.4733 GB/s .. 1.5849 GB/s]
SIMD_json_borrow                         Avg: 1.2579 GB/s (-1.86%)    Median: 1.2729 GB/s (-1.49%)    [990.58 MB/s .. 1.2995 GB/s]
gh-archive
serde_json                               Avg: 538.63 MB/s (-0.32%)     Median: 539.07 MB/s (-1.29%)     [525.76 MB/s .. 549.05 MB/s]
serde_json + access by key               Avg: 540.55 MB/s (-0.76%)     Median: 541.39 MB/s (-0.84%)     [521.75 MB/s .. 548.68 MB/s]
serde_json_borrow                        Avg: 1.0849 GB/s (+23.13%)    Median: 1.0873 GB/s (+22.41%)    [1.0266 GB/s .. 1.1046 GB/s]
serde_json_borrow + access by key        Avg: 1.0663 GB/s (+20.30%)    Median: 1.0825 GB/s (+22.08%)    [759.26 MB/s .. 1.1250 GB/s]
SIMD_json_borrow                         Avg: 1.0522 GB/s (-0.77%)     Median: 1.0539 GB/s (-0.71%)     [1.0142 GB/s .. 1.0696 GB/s]
jszwec added a commit to jszwec/serde_json_borrow that referenced this issue Jan 19, 2025
serde by always creates Cow::Owned even if it's possible to Borrow:
serde-rs/serde#1852 (comment)

We create our own wrapper around Cow in order to allow Borrowed keys.

This significantly improves performance:
parse
simple_json
serde_json                               Avg: 283.92 MB/s (-0.84%)     Median: 284.16 MB/s (-0.26%)     [279.00 MB/s .. 289.83 MB/s]
serde_json + access by key               Avg: 276.34 MB/s (-1.24%)     Median: 276.46 MB/s (-1.11%)     [267.63 MB/s .. 284.46 MB/s]
serde_json_borrow                        Avg: 534.96 MB/s (+19.37%)    Median: 533.52 MB/s (+18.98%)    [517.16 MB/s .. 561.15 MB/s]
serde_json_borrow + access by key        Avg: 530.26 MB/s (+18.52%)    Median: 529.16 MB/s (+18.28%)    [519.93 MB/s .. 544.36 MB/s]
SIMD_json_borrow                         Avg: 227.52 MB/s (-0.40%)     Median: 229.19 MB/s (+0.40%)     [184.71 MB/s .. 237.90 MB/s]
hdfs
serde_json                               Avg: 573.25 MB/s (-1.02%)     Median: 573.38 MB/s (-0.78%)     [549.59 MB/s .. 587.74 MB/s]
serde_json + access by key               Avg: 610.33 MB/s (-0.88%)     Median: 609.03 MB/s (-1.00%)     [587.33 MB/s .. 631.20 MB/s]
serde_json_borrow                        Avg: 1.0051 GB/s (+19.36%)    Median: 1.0037 GB/s (+18.06%)    [978.71 MB/s .. 1.0418 GB/s]
serde_json_borrow + access by key        Avg: 1.0453 GB/s (+20.75%)    Median: 1.0432 GB/s (+20.01%)    [1.0123 GB/s .. 1.0802 GB/s]
SIMD_json_borrow                         Avg: 551.42 MB/s (+0.26%)     Median: 554.60 MB/s (+0.91%)     [478.56 MB/s .. 566.50 MB/s]
hdfs_with_array
serde_json                               Avg: 470.84 MB/s (-0.24%)    Median: 470.70 MB/s (-0.20%)    [460.56 MB/s .. 480.44 MB/s]
serde_json + access by key               Avg: 471.89 MB/s (+0.09%)    Median: 471.92 MB/s (-0.03%)    [459.45 MB/s .. 484.84 MB/s]
serde_json_borrow                        Avg: 813.40 MB/s (+7.66%)    Median: 816.02 MB/s (+7.78%)    [798.52 MB/s .. 834.84 MB/s]
serde_json_borrow + access by key        Avg: 821.93 MB/s (+7.64%)    Median: 822.22 MB/s (+7.67%)    [786.16 MB/s .. 838.18 MB/s]
SIMD_json_borrow                         Avg: 458.26 MB/s (-0.15%)    Median: 457.11 MB/s (-0.39%)    [445.08 MB/s .. 473.40 MB/s]
wiki
serde_json                               Avg: 1.2361 GB/s (-0.93%)    Median: 1.2367 GB/s (-1.24%)    [1.2083 GB/s .. 1.2777 GB/s]
serde_json + access by key               Avg: 1.2717 GB/s (-0.63%)    Median: 1.2715 GB/s (-1.91%)    [1.1773 GB/s .. 1.3294 GB/s]
serde_json_borrow                        Avg: 1.4936 GB/s (+3.09%)    Median: 1.4950 GB/s (+3.02%)    [1.4339 GB/s .. 1.5327 GB/s]
serde_json_borrow + access by key        Avg: 1.5255 GB/s (+3.51%)    Median: 1.5266 GB/s (+3.13%)    [1.4733 GB/s .. 1.5849 GB/s]
SIMD_json_borrow                         Avg: 1.2579 GB/s (-1.86%)    Median: 1.2729 GB/s (-1.49%)    [990.58 MB/s .. 1.2995 GB/s]
gh-archive
serde_json                               Avg: 538.63 MB/s (-0.32%)     Median: 539.07 MB/s (-1.29%)     [525.76 MB/s .. 549.05 MB/s]
serde_json + access by key               Avg: 540.55 MB/s (-0.76%)     Median: 541.39 MB/s (-0.84%)     [521.75 MB/s .. 548.68 MB/s]
serde_json_borrow                        Avg: 1.0849 GB/s (+23.13%)    Median: 1.0873 GB/s (+22.41%)    [1.0266 GB/s .. 1.1046 GB/s]
serde_json_borrow + access by key        Avg: 1.0663 GB/s (+20.30%)    Median: 1.0825 GB/s (+22.08%)    [759.26 MB/s .. 1.1250 GB/s]
SIMD_json_borrow                         Avg: 1.0522 GB/s (-0.77%)     Median: 1.0539 GB/s (-0.71%)     [1.0142 GB/s .. 1.0696 GB/s]
jszwec added a commit to jszwec/serde_json_borrow that referenced this issue Jan 19, 2025
serde by always creates Cow::Owned even if it's possible to Borrow:
serde-rs/serde#1852 (comment)

We create our own wrapper around Cow in order to allow Borrowed keys.

This significantly improves performance:
parse
simple_json
serde_json                               Avg: 283.92 MB/s (-0.84%)     Median: 284.16 MB/s (-0.26%)     [279.00 MB/s .. 289.83 MB/s]
serde_json + access by key               Avg: 276.34 MB/s (-1.24%)     Median: 276.46 MB/s (-1.11%)     [267.63 MB/s .. 284.46 MB/s]
serde_json_borrow                        Avg: 534.96 MB/s (+19.37%)    Median: 533.52 MB/s (+18.98%)    [517.16 MB/s .. 561.15 MB/s]
serde_json_borrow + access by key        Avg: 530.26 MB/s (+18.52%)    Median: 529.16 MB/s (+18.28%)    [519.93 MB/s .. 544.36 MB/s]
SIMD_json_borrow                         Avg: 227.52 MB/s (-0.40%)     Median: 229.19 MB/s (+0.40%)     [184.71 MB/s .. 237.90 MB/s]
hdfs
serde_json                               Avg: 573.25 MB/s (-1.02%)     Median: 573.38 MB/s (-0.78%)     [549.59 MB/s .. 587.74 MB/s]
serde_json + access by key               Avg: 610.33 MB/s (-0.88%)     Median: 609.03 MB/s (-1.00%)     [587.33 MB/s .. 631.20 MB/s]
serde_json_borrow                        Avg: 1.0051 GB/s (+19.36%)    Median: 1.0037 GB/s (+18.06%)    [978.71 MB/s .. 1.0418 GB/s]
serde_json_borrow + access by key        Avg: 1.0453 GB/s (+20.75%)    Median: 1.0432 GB/s (+20.01%)    [1.0123 GB/s .. 1.0802 GB/s]
SIMD_json_borrow                         Avg: 551.42 MB/s (+0.26%)     Median: 554.60 MB/s (+0.91%)     [478.56 MB/s .. 566.50 MB/s]
hdfs_with_array
serde_json                               Avg: 470.84 MB/s (-0.24%)    Median: 470.70 MB/s (-0.20%)    [460.56 MB/s .. 480.44 MB/s]
serde_json + access by key               Avg: 471.89 MB/s (+0.09%)    Median: 471.92 MB/s (-0.03%)    [459.45 MB/s .. 484.84 MB/s]
serde_json_borrow                        Avg: 813.40 MB/s (+7.66%)    Median: 816.02 MB/s (+7.78%)    [798.52 MB/s .. 834.84 MB/s]
serde_json_borrow + access by key        Avg: 821.93 MB/s (+7.64%)    Median: 822.22 MB/s (+7.67%)    [786.16 MB/s .. 838.18 MB/s]
SIMD_json_borrow                         Avg: 458.26 MB/s (-0.15%)    Median: 457.11 MB/s (-0.39%)    [445.08 MB/s .. 473.40 MB/s]
wiki
serde_json                               Avg: 1.2361 GB/s (-0.93%)    Median: 1.2367 GB/s (-1.24%)    [1.2083 GB/s .. 1.2777 GB/s]
serde_json + access by key               Avg: 1.2717 GB/s (-0.63%)    Median: 1.2715 GB/s (-1.91%)    [1.1773 GB/s .. 1.3294 GB/s]
serde_json_borrow                        Avg: 1.4936 GB/s (+3.09%)    Median: 1.4950 GB/s (+3.02%)    [1.4339 GB/s .. 1.5327 GB/s]
serde_json_borrow + access by key        Avg: 1.5255 GB/s (+3.51%)    Median: 1.5266 GB/s (+3.13%)    [1.4733 GB/s .. 1.5849 GB/s]
SIMD_json_borrow                         Avg: 1.2579 GB/s (-1.86%)    Median: 1.2729 GB/s (-1.49%)    [990.58 MB/s .. 1.2995 GB/s]
gh-archive
serde_json                               Avg: 538.63 MB/s (-0.32%)     Median: 539.07 MB/s (-1.29%)     [525.76 MB/s .. 549.05 MB/s]
serde_json + access by key               Avg: 540.55 MB/s (-0.76%)     Median: 541.39 MB/s (-0.84%)     [521.75 MB/s .. 548.68 MB/s]
serde_json_borrow                        Avg: 1.0849 GB/s (+23.13%)    Median: 1.0873 GB/s (+22.41%)    [1.0266 GB/s .. 1.1046 GB/s]
serde_json_borrow + access by key        Avg: 1.0663 GB/s (+20.30%)    Median: 1.0825 GB/s (+22.08%)    [759.26 MB/s .. 1.1250 GB/s]
SIMD_json_borrow                         Avg: 1.0522 GB/s (-0.77%)     Median: 1.0539 GB/s (-0.71%)     [1.0142 GB/s .. 1.0696 GB/s]

Fixes PSeitz#31.
jszwec added a commit to jszwec/serde_json_borrow that referenced this issue Jan 19, 2025
serde always creates Cow::Owned even if it's possible to Borrow:
serde-rs/serde#1852 (comment)

We create our own wrapper around Cow in order to allow Borrowed keys.

This significantly improves performance:
parse
simple_json
serde_json                               Avg: 283.92 MB/s (-0.84%)     Median: 284.16 MB/s (-0.26%)     [279.00 MB/s .. 289.83 MB/s]
serde_json + access by key               Avg: 276.34 MB/s (-1.24%)     Median: 276.46 MB/s (-1.11%)     [267.63 MB/s .. 284.46 MB/s]
serde_json_borrow                        Avg: 534.96 MB/s (+19.37%)    Median: 533.52 MB/s (+18.98%)    [517.16 MB/s .. 561.15 MB/s]
serde_json_borrow + access by key        Avg: 530.26 MB/s (+18.52%)    Median: 529.16 MB/s (+18.28%)    [519.93 MB/s .. 544.36 MB/s]
SIMD_json_borrow                         Avg: 227.52 MB/s (-0.40%)     Median: 229.19 MB/s (+0.40%)     [184.71 MB/s .. 237.90 MB/s]
hdfs
serde_json                               Avg: 573.25 MB/s (-1.02%)     Median: 573.38 MB/s (-0.78%)     [549.59 MB/s .. 587.74 MB/s]
serde_json + access by key               Avg: 610.33 MB/s (-0.88%)     Median: 609.03 MB/s (-1.00%)     [587.33 MB/s .. 631.20 MB/s]
serde_json_borrow                        Avg: 1.0051 GB/s (+19.36%)    Median: 1.0037 GB/s (+18.06%)    [978.71 MB/s .. 1.0418 GB/s]
serde_json_borrow + access by key        Avg: 1.0453 GB/s (+20.75%)    Median: 1.0432 GB/s (+20.01%)    [1.0123 GB/s .. 1.0802 GB/s]
SIMD_json_borrow                         Avg: 551.42 MB/s (+0.26%)     Median: 554.60 MB/s (+0.91%)     [478.56 MB/s .. 566.50 MB/s]
hdfs_with_array
serde_json                               Avg: 470.84 MB/s (-0.24%)    Median: 470.70 MB/s (-0.20%)    [460.56 MB/s .. 480.44 MB/s]
serde_json + access by key               Avg: 471.89 MB/s (+0.09%)    Median: 471.92 MB/s (-0.03%)    [459.45 MB/s .. 484.84 MB/s]
serde_json_borrow                        Avg: 813.40 MB/s (+7.66%)    Median: 816.02 MB/s (+7.78%)    [798.52 MB/s .. 834.84 MB/s]
serde_json_borrow + access by key        Avg: 821.93 MB/s (+7.64%)    Median: 822.22 MB/s (+7.67%)    [786.16 MB/s .. 838.18 MB/s]
SIMD_json_borrow                         Avg: 458.26 MB/s (-0.15%)    Median: 457.11 MB/s (-0.39%)    [445.08 MB/s .. 473.40 MB/s]
wiki
serde_json                               Avg: 1.2361 GB/s (-0.93%)    Median: 1.2367 GB/s (-1.24%)    [1.2083 GB/s .. 1.2777 GB/s]
serde_json + access by key               Avg: 1.2717 GB/s (-0.63%)    Median: 1.2715 GB/s (-1.91%)    [1.1773 GB/s .. 1.3294 GB/s]
serde_json_borrow                        Avg: 1.4936 GB/s (+3.09%)    Median: 1.4950 GB/s (+3.02%)    [1.4339 GB/s .. 1.5327 GB/s]
serde_json_borrow + access by key        Avg: 1.5255 GB/s (+3.51%)    Median: 1.5266 GB/s (+3.13%)    [1.4733 GB/s .. 1.5849 GB/s]
SIMD_json_borrow                         Avg: 1.2579 GB/s (-1.86%)    Median: 1.2729 GB/s (-1.49%)    [990.58 MB/s .. 1.2995 GB/s]
gh-archive
serde_json                               Avg: 538.63 MB/s (-0.32%)     Median: 539.07 MB/s (-1.29%)     [525.76 MB/s .. 549.05 MB/s]
serde_json + access by key               Avg: 540.55 MB/s (-0.76%)     Median: 541.39 MB/s (-0.84%)     [521.75 MB/s .. 548.68 MB/s]
serde_json_borrow                        Avg: 1.0849 GB/s (+23.13%)    Median: 1.0873 GB/s (+22.41%)    [1.0266 GB/s .. 1.1046 GB/s]
serde_json_borrow + access by key        Avg: 1.0663 GB/s (+20.30%)    Median: 1.0825 GB/s (+22.08%)    [759.26 MB/s .. 1.1250 GB/s]
SIMD_json_borrow                         Avg: 1.0522 GB/s (-0.77%)     Median: 1.0539 GB/s (-0.71%)     [1.0142 GB/s .. 1.0696 GB/s]

Fixes PSeitz#31.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

8 participants