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"
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.
#[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:
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.