Lesson 8.1 · 9 min
Messages become JSON
A real model does not understand a Rust struct. It understands JSON, a plain-text format that every language can read. This lesson teaches Chatterbox to turn its Message into JSON and back, using a crate called serde.
Adds to ChatterboxMessages become JSON.
Why JSON
Everything Chatterbox has done so far stayed inside one program. A model lives somewhere else, on another computer, so the two have to agree on how to write a message down as text that travels over a network. That text is almost always JSON: curly braces for a record, square brackets for a list, and quoted names for fields.
Our Message has a role (who is talking) and some content (what they said). As JSON it looks like {"role":"user","content":"hi"}. Writing a struct down as text is called serializing. Reading text back into a struct is deserializing.
Crates: code other people wrote
Rust's standard library does not know JSON. A crate is a package of Rust code someone else wrote, and you ask for it by name in Cargo.toml, the file you met in lesson 1.2. Two crates do the work here: serde knows how to turn Rust values into and out of formats, and serde_json teaches it the JSON format.
From inside your project folder, cargo add serde --features derive and cargo add serde_json add both lines to Cargo.toml for you. The derive feature is the one that lets you write one line above a struct and get all the parsing code written for you.
One line above the struct
Here is a Message with two new words on it: Serialize and Deserialize. Those words in #[derive(...)] ask serde to write the conversion code. You do not write any of it.
One change from chapter 5, where the fields were author and text: for the API we rename them to role and content, because those are the names a model's API expects. The field names become the JSON keys, so they have to match.
$ cargo run
{"role":"user","content":"hi"}
Message { role: "user", content: "hi" }Which text does this program print?
{"role":"assistant","content":"Hello!"}{"content":"Hello!","role":"assistant"}Message { role: "assistant", content: "Hello!" }
Reveal the answer
The program prints: {"role":"assistant","content":"Hello!"} serde writes the fields in the order the struct declares them, with the field names as quoted keys. It is JSON text, not the debug form of the struct.
Reading can fail
Writing a Message as JSON always works: the struct is already shaped correctly. Reading is different, because the text came from outside. It might be missing a field, or not be JSON at all. That is why from_str returns a Result, the type from chapter 7. In the program above we used unwrap() to keep things short. In a real program you match on it, or use ?, so a bad reply becomes an error message instead of a crash.
This is the boundary where Rust earns its keep. If a model returns {"role":"assistant"} with no content, you find out right there, at the edge. You never carry a half-filled Message through the rest of the program.
Fill in to_json so it returns the JSON text for a Message, and from_json so it returns Some(message) for good JSON and None for text that does not fit. .ok() on a Result gives an Option: Ok(v) becomes Some(v) and Err(_) becomes None.
Fill in to_json so it returns the JSON text for a Message, and from_json so it returns Some(message) for good JSON and None for text that does not fit. .ok() on a Result gives an Option: Ok(v) becomes Some(v) and Err(_) becomes None.
Hidden tests (4): not_json_is_none writes_expected_json round_trip_gives_same_message missing_field_is_none
This exercise runs real Rust in your browser through the official Rust Playground, so it needs JavaScript.
What you have now
Chatterbox can write a Message as JSON and read one back. That is half of talking to a model: the request goes out as JSON and the reply comes back as JSON. Next lesson, we put a name on "the thing that produces replies", so the same chat loop can use a fake one or a real one.
Say it out loud, then reveal
What are serializing and deserializing?Reveal
Why does <code>from_str</code> return a Result when <code>to_string</code> practically never fails?Reveal
What does <code>#[derive(Serialize, Deserialize)]</code> do?Reveal
That is lesson 8.1. Tick it off so the course remembers where you are.
Go deeper
Questions people ask
What is JSON?
A plain-text format for data: curly braces for records, square brackets for lists, and quoted field names. Nearly every web API, including model APIs, uses it.
What is a crate in Rust?
A package of Rust code. You list the crates your project needs in Cargo.toml and cargo downloads and builds them.
Do I need serde to use JSON in Rust?
In practice yes. The standard library has no JSON support, and serde with serde_json is the common choice.
Sources serde · serde_json on docs.rs