How do I get the version of workspace member using a method from the parent member?

Viewed 147

This question has a trash repo with minimum reproducible code.

How to reproduce

I have created the project like this:

cargo new foo --bin
cd foo
cargo new bar --bin

Then, I have updated Cargo.toml as below:

[package]
name = "foo"
version = "0.1.0" # notice the version
edition = "2018"

[dependencies]

[workspace]
members = ["bar"]

And then, I have updated bar/Cargo.toml seen as below:

[package]
name = "bar"
version = "0.2.0" # notice the version
edition = "2018"

[dependencies]
foo = { path = ".." }

Notice the versions of both Cargo.toml.

Then, I have created a function in src/lib.rs like this:

pub fn get_version<'a>() -> &'a str {
    env!("CARGO_PKG_VERSION")
}

Then, in bar/src/main.rs, I used it as below:

use foo::get_version;

fn main() {
    println!("{}", get_version());
}

Then I run cargo run -p bar.

The Problem

I will possibly have multiple workspace members that will use foo::get_version. I expect them to print out their own versions. In this example, foo is 0.1.0 and bar is 0.2.0.

So, I want the main function of bar to print out 0.2.0, yet it prints out 0.1.0, which is expected because env! macro takes environment variable and directly passes it into final binary. When I compile, it compiles foo::get_version, which is inside foo and CARGO_PKG_VERSION is 0.1.0 on compile time.

However, this is not the behavior I desire. I desire it to get the version of the crate that get_version is used and I want to keep it in foo because, again, I'd like to reuse it again and again on possible different workspace members.

Hence the question, how do I get the version of workspace member using a method from the parent member?


Environment

  • rustc 1.55.0
1 Answers

This cannot possibly work. Functions cannot be generic over their calling contexts.

You can get some info regarding who called the function at run-time by using #[track_caller]. However, that doesn't help that the information you want can only be retrieved in the context of the individual crates at compile-time anyway.

Your only recourse is to use a macro:

macro_rules! get_version {
    () => {
        env!("CARGO_PKG_VERSION")
    }
}

With a macro, code is generated at the call-site, which means that, if called in one of the "child" crates, env!("CARGO_PKG_VERSION") will retrieve the "child" crate's version. You can of course write more code in the macro if the goal of the original function was intended to encompass more logic into the "parent" crate. But that's the only way to get access to crate-specific info like this.

Related