Description
Bug Report
Version
Builds fine:
tracing-regression v0.1.0 (/local/home/jongje/dev/tmp/tracing-regression)
└── tracing v0.1.37
├── tracing-attributes v0.1.24 (proc-macro)
└── tracing-core v0.1.30
Is broken:
tracing-regression v0.1.0 (/local/home/jongje/dev/tmp/tracing-regression)
└── tracing v0.1.38
├── tracing-attributes v0.1.24 (proc-macro)
└── tracing-core v0.1.30
Platform
Linux host 5.4.235-151.344.amzn2int.aarch64 #1 SMP Sat Mar 11 23:52:03 UTC 2023 aarch64 aarch64 aarch64 GNU/Linux
Crates
tracing
and tracing-attributes
(I believe a Drop
impl was added in the latter as well)
Description
The change in #2562 that was intended to fix #2541 is a breaking change (and indeed broke my code). In particular, we now have
https://github.com/ilslv/tracing/blob/73e73a9d7677a848c614c976ac176d56c889bb9b/tracing/src/instrument.rs#L307
whereas we didn't before. If you have impl Drop
for type with a generic type parameter T
, Rust assumes that the type T
may be used in the Drop
implementation, and therefore requires that T
outlives the wrapper type. This wasn't previously the case — without the Drop
, the T
only had to live as long as the last use of the wrapper, but crucially not all the way until it is dropped. You can see this with code like:
use std::task::{Context, Poll};
use tower_service::Service;
use tracing::instrument::Instrument;
pub struct Svc;
impl tower_service::Service<()> for Svc {
type Response = ();
type Error = ();
type Future = std::future::Ready<Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, _: ()) -> Self::Future {
std::future::ready(Ok(()))
}
}
pub async fn foo(s: tracing::Span, f: &mut Svc) {
let mut _ready = f.ready().instrument(s.clone());
// _ready is no longer used from this point on
//
// The type of _ready is Instrumented<Ready<&mut Svc>>
// Rust only permits the &mut Svc to be used if
// neither Ready nor Instrumented have a Drop impl.
// This is because otherwise the implicit drop(_ready)
// below might access &mut Svc, in which case any use
// in the interrim would be unsound.
//
// (
// Alternatively, `impl Drop` is permitted if
// each <T> is marked #[may_dangle] as per
// https://github.com/rust-lang/rust/issues/34761
// )
let _ = s.in_scope(|| f.call(()));
// implicit drop(_ready)
}
trait ServiceExt {
fn ready(&mut self) -> std::future::Ready<&'_ mut Self>
where
Self: Sized,
{
std::future::ready(self)
}
}
impl<T> ServiceExt for T where T: Service<()> {}
which will build fine with tracing 0.1.37
, but no longer builds with tracing 0.1.38
(which has this change):
error[E0500]: closure requires unique access to `*f` but it is already borrowed
--> src/lib.rs:37:24
|
22 | let mut _ready = f.ready().instrument(s.clone());
| --------- borrow occurs here
...
37 | let _ = s.in_scope(|| f.call(()));
| ^^ - second borrow occurs due to use of `*f` in closure
| |
| closure construction occurs here
38 | // implicit drop(_ready)
39 | }
| - first borrow might be used here, when `_ready` is dropped and runs the `Drop` code for type `Instrumented`
This likely warrants a yank if enough people run into it. It could just be me, though this was in fairly innocuous code. Tracing it back to this change also wasn't trivial, so some folks may run into this but not realize how the issue arose for them in the first place.
The change will either need to be rolled back, or we'll need to include a #[may_dangle]
annotation on the added impl Drop
.