Code works in Playground but not locally

The following code works fine when I run it in the Playground, but when I create a local project using cargo new and put this in main.rs, I get errors.

use serde::{Deserialize, Serialize}; // says these imports are unused
use serde_json;

#[derive(Deserialize, Serialize, Debug)] // says it cannot find the Deserialize and Serialize macros
struct Dog {
    name: String,
    breed: String,
}

fn main() {
    let mut dogs = Vec::new();
    dogs.push(Dog {
        name: "Comet".to_string(),
        breed: "Whippet".to_string(),
    });

    let json = serde_json::to_string(&dogs).unwrap();
    println!("json = {}", json);
    let dogs: Vec<Dog> = serde_json::from_str(&json).unwrap();
    println!("dogs = {:#?}", dogs);
}

Here is my cargo.toml file:

[package]
name = "serde-json"
version = "0.1.0"
authors = ["R. Mark Volkmann <r.mark.volkmann@gmail.com>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
serde = "1.0.118"
serde_json = "1.0.60"

(Playground)

Output:

json = [{"name":"Comet","breed":"Whippet"}]
dogs = [
    Dog {
        name: "Comet",
        breed: "Whippet",
    },
]

Errors:

   Compiling playground v0.0.1 (/playground)
    Finished dev [unoptimized + debuginfo] target(s) in 1.65s
     Running `target/debug/playground`

You need to enable the derive feature of Serde.

1 Like

You need to enable the derive feature for serde. Using derive · Serde

[dependencies]
serde = { version = "1.0", features = ["derive"] }
2 Likes

Wow, I don’t think I would have ever figured that out on my own.
So it’s not enough to list a crate as a dependency in order to use it?
I sometimes have to also indicate which parts of it I want to use inside cargo.toml?
I thought use statements were all I needed to indicate which parts I want to use.

use statements are only useful to bring stuff into scope, in order to allow using shorter paths: a Rust program can be written without use statements at all!

I like to use the filesystem analogy: a use statement is like a local symbolic link (ln -s …), it comes in handy since it allows to use a path to the "current directory" (current namespace) instead of having to repeat each time the real path of the file/item (in your case, ::serde::Serialize and ::serde::Deserialize).

Yes, in order to optimize compile times (and potentially the generated binary size too), crate authors can "guard" parts of their own crate / of their own API, so that, depending on compile-time "settings", these parts may not exist / be skipped and thus not be compiled. These compile-time "settings" are, mainly, Cargo features. We then say those parts of the APi have been "feature-gated" / gated by a feature.

image

#[derive(…)] (procedural) macros, for instance, have a non-negligible compile-time impact; it is thus customary to feature-gate those. That's what ::serde does: one needs to enable the "derive" feature in order to avoid ::serde skipping those.

  • By the way, these features can be enabled by default by the library author; that could have been a choice made by serde in order to simplify its design (downstream users can opt-out of enabled-by-default features by specifying default-features = false in the Cargo.toml), but in the case of ::serde, they've preferred not to.

In the case of ::serde, since it is the most pervasive crate out there, I highly recommend reading its associated guide / book, which has a dedicated section for the derives:

  • Using derive · Serde

2 Likes

You don't need to list features to use the base API of a crate - but they are needed if you want to use features that don't exist in the public API, such as macros in Serde's case.

1 Like