Stack Overflow With Serde And String.into() Arc<str>

Hey everybody! I have a small playground sample that causes a stack overflow. Does anybody know why or how to avoid it? This feels likea bug.

use std::sync::Arc;

#[derive(serde::Deserialize)]
#[serde(from = "String")]
pub struct FontFamily(Arc<str>);

impl From<String> for FontFamily {
    fn from(s: String) -> Self {
        s.into()
    }
}

#[derive(serde::Deserialize)]
struct Data {
    family: FontFamily
}

fn main() {
    let test: Data = serde_json::from_str(r#"{
        "family": "testing"
    }"#).unwrap();
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
warning: unused variable: `test`
  --> src/main.rs:19:9
   |
19 |     let test: Data = serde_json::from_str(r#"{
   |         ^^^^ help: if this is intentional, prefix it with an underscore: `_test`
   |
   = note: `#[warn(unused_variables)]` on by default

warning: field is never read: `family`
  --> src/main.rs:15:5
   |
15 |     family: FontFamily
   |     ^^^^^^^^^^^^^^^^^^
   |
   = note: `#[warn(dead_code)]` on by default

warning: `playground` (bin "playground") generated 2 warnings
    Finished dev [unoptimized + debuginfo] target(s) in 1.33s
     Running `target/debug/playground`

thread 'main' has overflowed its stack
fatal runtime error: stack overflow
timeout: the monitored command dumped core
/playground/tools/entrypoint.sh: line 11:     8 Aborted                 timeout --signal=KILL ${timeout} "$@"

This is an inifinite recursion.
Do:

impl From<String> for FontFamily {
    fn from(s: String) -> Self {
        FontFamily(s.into())
    }
}
2 Likes

Oh, I think I found it. I needed to do this instead:

impl From<String> for FontFamily {
    fn from(s: String) -> Self {
        Self(Arc::from(s))
    }
}

At first it was confusing that it even compiled in the first place, but now I'm realizing that the Deserialize implementation must have automatically created a From<String> implementation or something that was called when I called .into() which resulted in infinite recursion

Yep, makes sense. :slight_smile:
Oh, duh, because I'm implementing From, it's not from Deserialize. :man_facepalming:

OK cool.

I'm surprised that clippy didn't flag an infinite recursion here.
Do you use rust-analyzer? If so, is your check command cargo clippy?

Interestingly running clippy didn't cause any warning in the playground. I haven't tried it in my workspace again yet.

I do use rust analyzer but I never figured out how to get it to give me clippy warnings. I never thought of setting the check command to clippy instead of check! That tip was worth opening this topic for by itself, so thanks! :slight_smile: