Я пишу корзину Rust для внутреннего использования компанией, которая упаковывает превосходную коробку Tokio tracing
с небольшим количеством дополнительных функций. Я не только повторно экспортирую макросы в tracing
, но также добавляю несколько своих собственных, которые внутренне вызывают макросы tracing
. Моя цель - позволить всем нашим внутренним ящикам зависеть только от моего ящика-обертки, без необходимости явно вставлять tracing
зависимости в каждом ящике.
Это работает очень хорошо, пока я не столкнулся с проблемой сегодня мне потребовалось несколько часов, чтобы изолировать. Я сделал минимальный пример, чтобы показать поведение здесь .
В этом примере у меня есть рабочее пространство с двумя ящиками: my-logger-crate
, который упаковывает tracing
и предоставляет макрос print_trace!
, и двоичный ящик my-binary-crate
, который зависит от my-logger-crate
, и вызывает макрос внутри функции main
.
Cargo.toml
для my-logger-crate
очень прост; единственное, что я добавил к автоматически сгенерированному скелету, это зависимость tracing
:
[package]
name = "my-logger-crate"
version = "0.1.0"
authors = ["Adam Nelson <anelson@users.noreply.github.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
tracing = "0.1.12"
Это макрос my-logger-crate
:
/// Re-export everything in the `tracing` crate
pub use ::tracing::*;
/// A version of the `trace!` macro that sets a special target
#[macro_export]
macro_rules! print_trace {
($($arg:tt)*) => (
// Invoke the `trace!` macro which is defined in the `tracing` crate but which has been
// re-exported from this crate, so that downstream callers don't have to take an explicit
// dependency on `tracing`
$crate::trace!(target: "console", $($arg)*)
)
}
Используется my-binary-crate
, чей Cargo.toml
также прост:
[package]
name = "my-binary-crate"
version = "0.1.0"
authors = ["Adam Nelson <anelson@users.noreply.github.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
my-logger-crate = { path = "../my-logger-crate" }
# If the following line is uncommented, then the call to `print_trace!` in `main()` fails to compile.
# tower = "0.3"
А вот функция main()
в my-binary-crate
:
fn main() {
println!("Hello, world!");
// Log a trace event with the logging system
my_logger_crate::print_trace!("this is a trace message from print_trace!");
}
Если my-binary-crate
не имеет конфликтующих зависимости, это компилируется и работает нормально.
Но посмотрите, что произойдет, если у нас есть зависимость от tower
в файле my-binary-crate/Cargo.toml
:
[package]
name = "my-binary-crate"
version = "0.1.0"
authors = ["Adam Nelson <anelson@users.noreply.github.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
my-logger-crate = { path = "../my-logger-crate" }
# If the following line is uncommented, then the call to `print_trace!` in `main()` fails to compile.
tower = "0.3"
Вот что происходит:
Compiling my-binary-crate v0.1.0 (/home/cornelius/scrap/e2780c4bd3dfa24758fcfd93e225b100/my-binary-crate)
error[E0433]: failed to resolve: use of undeclared type or module `tracing`
--> my-binary-crate/src/main.rs:5:5
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of undeclared type or module `tracing`
|
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
Макрос print_trace!
расширяется до макроса tracing::trace!
, который затем расширяется до tracing::event!
, а затем расширяется до кода для регистрации события, которое включает в себя звонки на tracing::...
. Все это прекрасно работает, прежде чем я добавлю эту tower
зависимость, поэтому ясно, что компилятор может правильно разрешить tracing::...
, даже если my-binary-crate
не имеет прямой зависимости от tracing
.
Я подозреваю, что это связано с конфликтом зависимостей. tower
извлекает длинное дерево зависимостей, включая tower-buffer
, которое само зависит от tracing = "0.1.2"
.
Мое понимание того, как Car go разрешает этот кажущийся конфликт, заключается в том, что он будет использовать tracing
версию 0.1.12
, поскольку мой ящик для журналов явно зависит от версии =0.1.12
, а tower-buffer
указывает 0.1.2
, что эквивалентно ^0.1.2
. Но я не понимаю, почему добавление этого дополнительного дерева зависимостей нарушает использование моего макроса.
На данный момент я обошел его, добавив явные зависимости на tracing
во все нижележащие ящики, которые используют мой ящик для журналов, но это далеко от идеала.
Я бежал с включенной трассировкой макросов:
$ RUSTFLAGS="-Z macro-backtrace" cargo +nightly test
Compiling my-binary-crate v0.1.0 (/home/cornelius/scrap/e2780c4bd3dfa24758fcfd93e225b100/my-binary-crate)
error[E0433]: failed to resolve: use of undeclared type or module `tracing`
--> <::tracing::macros::__mk_format_args macros>:78:17
|
1 | / (@ { $ (,) * $ ($ out : expr), * $ (,) * }, $ fmt : expr, fields : $ (,) *) =>
2 | | { format_args ! ($ fmt, $ ($ out), *) } ;
3 | | (@ { $ (,) * $ ($ out : expr), * }, $ fmt : expr, fields : $ ($ k : ident) . +
4 | | = ? $ val : expr $ (,) *) =>
... |
78 | | (@ { }, tracing :: __mk_format_string ! ($ ($ kv) *), fields : $
| | ^^^^^^^ use of undeclared type or module `tracing`
79 | | ($ kv) *)
80 | | }
81 | | } ;
... [a lot of macro expansions snipped]
Неудивительно, что ошибка - это попытка вызвать tracing::__mk_format_string!
, которая терпит неудачу, потому что нет такого ящика tracing
, на который ссылается my-binary-crate
. Странно то, что для получения этой ошибки нужно было проанализировать несколько других макросов в tracing
. Возможно, это ошибка в tracing
, и она должна быть $crate::__mk_format_string!
. Однако я до сих пор не понимаю, почему это работает, пока я не добавлю tower
зависимость от my-binary-crate
.