How to set dependencies for a macro in Rust? - rust

I wrote a macro in a crate and import it in the main function of a main project.
The macro depends on:
use chrono::{Utc, Local, DateTime, Date};
Currently I include the dependency in the Cargo.toml of the main project.
Will I need to do the same for all main projects? Or Can I declare the dependency in the Cargo.toml of the macro crate?
//lib.rs //Mymacro
#[macro_export]
macro_rules! time {
() => {
use chrono::{Utc, Local, DateTime, Date};
let local_datetime: DateTime<Local> = Local::now();
println!("{:?}",local_datetime);
}
}
//main.rs // main project
extern crate Mymacro;
#[macro_use]
fn main() {
time!();
}
// Cargo.html
[dependencies]
chrono=""

Will I need to do the same for all main projects? Or Can I declare the dependency in the Cargo.toml of the macro crate?
You can, by either (1) having your macro call a function in your library or (2) by re-exporting chrono (partially or completely).
Let's consider that we have a workspace, with a lib and bin crate.
[workspace]
members = [
"bin",
"lib",
]
Now in your Cargo.toml for the lib crate you'd include chrono as a dependency, like you otherwise would.
[dependencies]
chrono = "0.4"
While the Cargo.toml for the bin crate only has the dependency of lib.
[dependencies]
lib = { path = "../lib" }
Now, let's consider that main.rs in the bin crate looks like this:
use lib::time;
fn main() {
time!();
}
It will remain the same, regardless of which option you go with.
The following snippets represent lib.rs.
Library Function
If you never actually return any chrono specific types, then this might be the easiest. As you avoid re-exporting chrono completely.
use chrono::{DateTime, Local};
pub fn _time() {
let local_datetime: DateTime<Local> = Local::now();
println!("{:?}", local_datetime);
}
#[macro_export]
macro_rules! time {
() => {
$crate::_time();
};
}
If you don't want _time to appear in the documentation of your library, then you can use the #[doc(hidden)] attribute.
Re-export some items
Alternatively, if you want everything to remain within the macro, then you can re-export the chrono types it uses, and prepend the types in your macro with $crate::.
The downside to this, is that if your bin crate needs a chrono type that is not re-exported, then that can be an "annoyance" and thus bin would need to depend on chrono = "0.4" anyways.
pub use chrono::{DateTime, Local};
#[macro_export]
macro_rules! time {
() => {
let local_datetime: $crate::DateTime<$crate::Local> = $crate::Local::now();
println!("{:?}", local_datetime);
};
}
In main.rs if you instead do use lib::*;, then you'd of course not need to prepend $crate::, but it's best to avoid doing use lib::*;.
Re-export Chrono
Lastly, if you need the chrono types in you bin crate. Then you can also re-export the whole chrono crate with pub extern crate chrono;.
Note that this time, you need to prepend the types in your macro with $crate::chrono::.
pub extern crate chrono;
#[macro_export]
macro_rules! time {
() => {
let local_datetime: $crate::chrono::DateTime<$crate::chrono::Local> =
$crate::chrono::Local::now();
println!("{:?}", local_datetime);
};
}

Related

How to benchmark rust library with cargo features?

I am working on a library that is configurable with cargo features, and I can't figure out how to get the bencher crate (for benchmarking) to work. In lib.rs I have
#[cfg(feature = "single_threaded")]
pub fn my_function() {...}
In benches/bench.rs I have
#[macro_use]
extern crate bencher;
extern crate my_crate;
use bencher::Bencher;
use my_crate::*;
fn single_thread(bench: &mut Bencher) {
bench.iter(|| {
for _ in 0..10 {
my_function();
}
})
}
benchmark_group!(benches, single_thread);
benchmark_main!(benches);
As is, the compiler says that it cannot find my_function because I haven't specified a configuration. If I add #[cfg(feature = "single_threaded")] above fn single_thread(), it can then find my_function, but that seems to put single_thread in a different context from everything else, such that the two macros at the bottom cannot find single_thread().
If I add #[cfg(feature = "single_threaded")] above each of the two macros, the compiler says to "consider adding a main function to benches/bench.rs," but a main function is added by benchmark_main!. If I put the entire file into a module and declare #[cfg(feature = "single_threaded")] once for the whole module, I get the same error about not having a main function. Any suggestions?
Oh and my Cargo.toml looks like this
[package]
name = "my_crate"
version = "0.1.0"
edition = "2021"
authors = ["Me"]
[dependencies]
[dev-dependencies]
bencher = "0.1.5"
[features]
single_threaded = []
[[bench]]
name = "benches"
harness = false

Selective model lib

how can i have crate that depend on with feature use another version of same crate like below
[features]
serde_1 = ["dep_serde_1"]
serde_1_0_133 = ["dep_serde_1_0_133"]
[dependencies]
dep_serde_1 = { package = "serde", version = "1.0.0", optional = true}
dep_serde_1_0_133 = { package = "serde", version = "1.0.133", optional = true}
my problem is compiler force me to use
use dep_serde_1::*;
except like this
use serde::*;
i just enable one of them at time and in code with cfg(feature = serde_1) i'll chose what code must compile
sorry about my poor English
[Edit]
main idea drive form this problem, for example
if my model use actix-0.10 and another crate that use my lib use actix-0.12 it generate compiler error
I'm not quite sure I understand what you want. If you want the name of the crate to be serde in your use statements, you can rename them back:
#[cfg(feature = "dep_serde_1")]
extern crate dep_serde_1 as serde;
#[cfg(feature = "dep_serde_1_0_133")]
extern crate dep_serde_1_0_133 as serde;
// Now you can
use serde::*;
[Edit:] The above takes care of your use of serde, but serde_derive has its own ideas. When you define a struct like
#[derive(Serialize)]
struct Asdf { /* … */ }
serde generates code that looks roughly like this:
const _: () = {
extern crate serde as _serde;
#[automatically_derived]
impl _serde::Serialize for Asdf { /* …
i.e. it ignores the renaming of the serde crate and tries to use the crate by its original name.
You can override this behavior with the crate container attribute:
#[derive(Serialize)]
#[serde(crate = "serde")]
struct Asdf { /* … */ }
which will make the generated code use the serde from the outer namespace:
const _: () = {
use serde as _serde;
#[automatically_derived]
impl serde::Serialize for Asdf {
Note, that mutually exclusive features are not a good idea. If you can, it's better to make one the default and the other to override it.
I'm also not sure your cargo dependencies are actually doing what you want them to. Cargo doesn't allow depending on a crate twice, and if I force dep_serde_1 to =1.0.0 cargo will complain that there is a conflict.

Unresolved import in Rust. Can't find crate [duplicate]

I have 4 files:
main.rs
mod bar;
fn main() {
let v = vec![1, 2, 3];
println!("Hello, world!");
}
lib.rs
pub mod foo;
pub mod bar;
foo.rs
pub fn say_foo() {
}
bar.rs
use crate::foo;
fn bar() {
foo::say_foo();
}
When I run cargo run I get an error saying:
error[E0432]: unresolved import `crate::foo`
--> src/bar.rs:1:5
|
1 | use crate::foo;
| ^^^^^^^^^^ no `foo` in the root
Could someone explain to me how to fix this? A bit more broadly: how does module lookup work when there's a main.rs and a lib.rs?
Edit: Adding mod foo to main.rs fixes the issue. But I don't understand this -- I was under the impression the lib.rs was the place that "exposed" all of my modules? Why do I have to declare the module in main.rs as well?
My Cargo.toml:
[package]
name = "hello-world"
version = "0.1.0"
authors = ["me#mgail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
Let's start from the beginning. Look at the Package Layout chapter in The Cargo Book. As you can see, your package can contain lot of stuff:
a binary (something you can run) or multiple binaries,
a single library (shared code),
example(s),
benchmark(s),
integration tests.
Package layout
Not all of the possibilities are listed here, just the binary / library combinations.
A binary
This is an example of a package with single binary. Entry point is the main function in the src/main.rs.
Cargo.toml:
[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
src/main.rs:
fn main() {
println!("Hallo, Rust here!")
}
$ cargo run
Hallo, Rust here!
A library
This is an example of a package with a library. Libraries don't have entry points, you can't run them. They're used for functionality sharing.
Cargo.toml:
[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
src/lib.rs:
pub fn foo() {
println!("Hallo, Rust library here!")
}
$ cargo run
error: a bin target must be available for `cargo run`
Do you see anything in the Cargo.toml file about a binary or a library? No. The reason is that I've followed the Package Layout and the cargo knows where to look for things.
A binary and a library
This is an example of a package with a binary and a library.
Cargo.toml:
[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
src/lib.rs:
pub const GREETING: &'static str = "Hallo, Rust library here!";
src/main.rs:
use hallo::GREETING;
fn main() {
println!("{}", GREETING);
}
Same question, do you see anything in the Cargo.toml file about a binary or a library? No.
This package contains two things:
a binary (root src/main.rs, entry point src/main.rs::main),
a library (root src/lib.rs, shared code).
A library can be referenced from the binary via use hallo::... where the hallo is this package name (Cargo.toml -> [package] -> name).
Your problem
Cargo.toml:
[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
Same package layout
A library part
src/lib.rs:
pub mod bar;
pub mod foo;
src/foo.rs:
pub fn say_foo() {
println!("Foo");
}
src/bar.rs:
use crate::foo;
pub fn bar() {
foo::say_foo();
}
crate refers to src/lib.rs, because we're in the context of our library here.
Treat it as a standalone unit and refer to it via use hallo::...; from the outside world.
A binary part
src/main.rs:
use hallo::bar::bar;
fn main() {
bar();
}
Here we're just using our library.
Without a library
Same code, but lib.rs was renamed to utils.rs and (foo|bar).rs files were moved to the src/utils/ folder.
src/utils.rs:
pub mod bar;
pub mod foo;
src/utils/foo.rs:
pub fn say_foo() {
println!("Foo");
}
src/utils/bar.rs:
use super::foo;
// or use crate::utils::foo;
pub fn bar() {
foo::say_foo();
}
We can use crate here as well, but because we're in the context of our binary, the path differs.
src/main.rs:
use utils::bar::bar;
mod utils;
fn main() {
bar();
}
Here we just declared another module (utils) and we're using it.
Summary
Cargo.toml content:
[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
If there's a src/main.rs file, you're basically saying this:
[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
[[bin]]
name = "hallo"
src = "src/main.rs"
If there's a src/lib.rs file, you're basically saying this:
[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
[lib]
name = "hallo"
path = "src/lib.rs"
If there're both of them, you're basically saying this:
[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
[[bin]]
name = "hallo"
path = "src/main.rs"
[lib]
name = "hallo"
path = "src/lib.rs"
Documentation
Package Layout
The Manifest Format
Managing Growing Projects with Packages, Crates, and Modules
In short the official Rust book has this to say:
If a package contains src/main.rs and src/lib.rs, it has two crates: a library and a binary, both with the same name as the package.
Furthermore the Rust reference says this:
crate resolves the path relative to the current crate
So there are actually two crates in your project, and to which crate the crate qualifier resolves to depends on where you call it.
Now in your code example, if you want things to compile you have to remove mod bar; from src/main.rs. Otherwise you'll be declaring that bar is a module within two crates.
After you remove that, then because in src/lib.rs you had:
pub mod foo;
pub mod bar;
bar would now be a module within src/lib.rs's crate, so the crate qualifier in bar.rs would then refer to src/lib.rs's hello-world crate, which is what you want.
One more thing, if you wanted to access items that are exposed in src/lib.rs from src/main.rs, you have to do as #zrzka said, which is to name the name of the crate that both src/lib.rs and src/main.rs share. For example, in your project which is named hello-world:
use hello_world::foo;
fn main() {
foo::say_foo();
}
is how you import the foo module declared in src/lib.rs into src/main.rs.
However it does appear that the importing behavior doesn't work the other way. I.e. if you declare some public module in src/main.rs, you can't import it into the src/lib.rs crate even when you specify the name of the crate. I couldn't find documentation describing this behavior but by testing it in Rust 1.37.0, it does appear to be the case.
The lib.rs and main.rs files are two independent entry points for your package.
When you use cargo run (or build the binary and run it explicitly), the entry point to be used is main.rs, and the crate keyword refer to the binary crate. It doesn't even have to know that there is something in lib.rs: the binary will treat the library as it would any other external crate, and it must be imported, through extern crate hello_world or, for example, use hello_world::foo.
When you import the library, however, the entry point is lib.rs, and the crate is the library crate. In this case, yes, all that you've added to lib.rs is exposed to the whole crate.
The usual worksflow in this case is to make the binary something like a thin wrapper around the library - in some extreme cases the main.rs would only contain something like
fn main() {
library::main();
}
and the whole logic (and all the project structure) goes into the library crate. One of the reasons is exactly what you've run into: the possible confusion whether this concrete module is imported in each crate in the package.
One more thing: mod defines a new module in your crate, whether a binary crate or library crate; while use only brings the module into the current scope.
In your example, use crate::foo in bar.rs tries to bring a module named foo under crate root into scope. But because there is no mod foo in main.rs, the foo module is not part of the binary crate.

How do I access exported functions inside a crate's "tests" directory?

How do I access my libraries exported functions inside the create's "tests" directory?
src/relations.rs:
#![crate_type = "lib"]
mod relations {
pub fn foo() {
println!("foo");
}
}
tests/test.rs:
use relations::foo;
#[test]
fn first() {
foo();
}
$ cargo test
Compiling relations v0.0.1 (file:///home/chris/github/relations)
/home/chris/github/relations/tests/test.rs:1:5: 1:14 error: unresolved import `relations::foo`. Maybe a missing `extern crate relations`?
/home/chris/github/relations/tests/test.rs:1 use relations::foo;
^~~~~~~~~
If I add the suggested extern crate relations, the error is:
/home/chris/github/relations/tests/test.rs:2:5: 2:19 error: unresolved import `relations::foo`. There is no `foo` in `relations`
/home/chris/github/relations/tests/test.rs:2 use relations::foo;
^~~~~~~~~~~~~~
I want to test my relations in this separate tests/test.rs file. How can I solve these use issues?
Your problem is that, first, mod relations is not public so it is not visible outside of the crate, and second, you don't import your crate in tests.
If you build your program with Cargo, then the crate name will be the one you defined in Cargo.toml. For example, if Cargo.toml looks like this:
[package]
name = "whatever"
authors = ["Chris"]
version = "0.0.1"
[lib]
name = "relations" # (1)
And src/lib.rs file contains this:
pub mod relations { // (2); note the pub modifier
pub fn foo() {
println!("foo");
}
}
Then you can write this in tests/test.rs:
extern crate relations; // corresponds to (1)
use relations::relations; // corresponds to (2)
#[test]
fn test() {
relations::foo();
}
The solution was to specify a crate_id at the top of src/relations.rs:
#![crate_id = "relations"]
#![crate_type = "lib"]
pub fn foo() {
println!("foo");
}
This seems to declare that all the contained code is part of a "relations" module, though I'm still not sure how this is different to the earlier mod block.

Deriving Encodable trait in a module not working

I have a main.rs file and a logging module inside logging.rs file. My file layout is:
.
├── Cargo.toml
├── src
│ ├── logging.rs
│ └── main.rs
The contents of my main.rs
mod logging;
fn main(){}
The contents of logging.rs
extern crate serialize;
use self::serialize::{json, Encoder, Encodable};
#[deriving(Encodable)]
pub struct Person {
pub age: i32
}
However this does not compile. The error is:
error: failed to resolve. Did you mean `self::serialize`?
/Users/valentin/../src/logging.rs:7 #[deriving(Encodable)]
Three questions:
Why does not it compile?
Why does moving the struct and use directive to main.rs makes it compile?
Why does changing serialize::Encodable to Show trait makes it compile even inside logging module?
However
If I add
extern crate serialize;
use self::serialize::{json, Encoder, Encodable};
to main.rs, it all starts compiling.
This is very confusing, the fourth questions is why isn't it sufficient to have only one extern crate + use serialize::.. inside logging module?
Let’s look at the code that’s generated, with rustc main.rs --pretty expanded:
#![feature(phase)]
#![no_std]
#![feature(globs)]
#[phase(plugin, link)]
extern crate std = "std";
extern crate rt = "native";
use std::prelude::*;
mod logging {
extern crate serialize;
use std::prelude::*;
use self::serialize::{json, Encoder, Encodable};
pub struct Person {
pub age: i32,
}
#[automatically_derived]
impl <__S: ::serialize::Encoder<__E>, __E>
::serialize::Encodable<__S, __E> for Person {
fn encode(&self, __arg_0: &mut __S) ->
::std::result::Result<(), __E> {
match *self {
Person { age: ref __self_0_0 } =>
__arg_0.emit_struct("Person", 1u, |_e| {
return _e.emit_struct_field("age", 0u,
|_e|
(*__self_0_0).encode(_e));
}),
}
}
}
}
fn main() { }
This demonstrates that the #[deriving(Encodable)]expands to stuff involving the paths ::serialize::*; that is, the item serialize from the crate root.
Now, extern crate serialize; from inside mod logging means that the path to serialize is ::logging::serialize, which is also accessible as self::serialize inside the module; there is no ::serialize.
The solution is moving the extern crate serialize; into the crate root. (This is where all extern crate definitions should be.) This is what fixed it for you, not the use self::serialize::{json, Encoder, Encodable};.

Resources