{"id":"0d65bce651f37765","repo":"tokio-rs/tokio","slug":"use-aiosource-register-borrowed-instead","errorCode":null,"errorMessage":"Use AioSource::register_borrowed instead","messagePattern":"Use AioSource::register_borrowed instead","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tokio/src/io/bsd/poll_aio.rs","lineNumber":32,"sourceCode":"use std::task::{ready, Context, Poll};\n\n/// Like [`mio::event::Source`], but for POSIX AIO only.\n///\n/// Tokio's consumer must pass an implementer of this trait to create a\n/// [`Aio`] object.  Implementers must implement at least one of [`AioSource::register`] and\n/// [`AioSource::register_borrowed`].\npub trait AioSource {\n    /// Registers this AIO event source with Tokio's reactor.\n    ///\n    /// # Safety\n    ///\n    /// It's memory-safe, but not I/O safe.  If the file referenced by `kq` gets dropped, then this\n    /// source may end up notifying the wrong file.\n    #[deprecated(since = \"1.52.0\", note = \"use register_borrowed instead\")]\n    fn register(&mut self, _kq: RawFd, _token: usize) {\n        // This default implementation exists so new AioSource implementers that implement the\n        // register_borrowed method can compile without the need to implement register.\n        unimplemented!(\"Use AioSource::register_borrowed instead\")\n    }\n\n    /// Registers this AIO event source with Tokio's reactor.\n    fn register_borrowed(&mut self, kq: BorrowedFd<'_>, token: usize) {\n        // This default implementation serves to provide backwards compatibility with AioSource\n        // implementers written before 1.52.0 that only implemented the unsafe `register` method.\n        #[allow(deprecated)]\n        self.register(kq.as_raw_fd(), token)\n    }\n\n    /// Deregisters this AIO event source with Tokio's reactor.\n    fn deregister(&mut self);\n}\n\n/// Wraps the user's AioSource in order to implement mio::event::Source, which\n/// is what the rest of the crate wants.\nstruct MioSource<T>(T);\n","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/tokio-rs/tokio/blob/108d6d3dc038332af2af83957748333091e35b3f/tokio/src/io/bsd/poll_aio.rs#L14-L50","documentation":"This message comes from an unimplemented!() call inside the default body of the deprecated AioSource::register method (poll_aio.rs:28-33). The trait was changed in Tokio 1.52.0 to favor register_borrowed (which takes a BorrowedFd and is I/O-safe); the old register(&mut self, kq: RawFd, token: usize) was deprecated and its default impl now panics with this message. The panic fires only if some code path actually invokes register() on an implementer that did not override it (i.e., an implementer that only implemented register_borrowed). Tokio's own MioSource wrapper (poll_aio.rs:51-79) exclusively calls register_borrowed, so internally Tokio will not trigger this; the panic is a guard against external callers or downstream crates that still call the unsafe register directly.","triggerScenarios":"Triggered by calling AioSource::register(kq, token) directly on a type that implements only register_borrowed (the post-1.52.0 style), so the default register body at poll_aio.rs:32 runs unimplemented!. This happens when: (1) a downstream crate's code still calls source.register(raw_fd, token) instead of register_borrowed(fd, token); (2) a generic helper written against the old trait API invokes register() unconditionally; (3) a trait object or generic that calls register() reaches the default impl because the implementer relies on the new method. Note: an implementer that overrides ONLY the old register() will still work via register_borrowed's backwards-compat default (poll_aio.rs:36-41), so the panic specifically implies the implementer overrode register_borrowed but not register, AND register() was called.","commonSituations":"Crate upgrade to Tokio >= 1.52.0 where a custom POSIX AIO source was migrated to register_borrowed but a separate helper still calls the old register(); copy-pasted code from pre-1.52 examples; trait objects dispatching to register() at runtime; FreeBSD/macOS AIO integrations (kqueue-based) that bypass Tokio's MioSource and call into the source directly; Cargo.toml version drift where the implementer and caller are in different crates updated independently.","solutions":["Replace direct calls to source.register(raw_fd, token) with source.register_borrowed(fd.as_fd(), token); this is the supported, I/O-safe path.","If you own the AioSource implementer, implement register_borrowed (and let the deprecated register stay at its default) — but make sure no code calls register() directly.","If you must support old Tokio (<1.52) and new, implement both register() and register_borrowed(); in the new method delegate properly and in the old method delegate via as_fd().","Audit generic helpers and trait objects for calls to register() and switch them to register_borrowed(); build with the deprecation warning enabled to catch usages.","Suppress nothing — fix the call site; the deprecation on register (since=\"1.52.0\") is the canonical signal."],"exampleFix":"// before: calling the deprecated, unsafe method directly (panics with\n// \"Use AioSource::register_borrowed instead\" on implementers that only\n// override register_borrowed)\nsource.register(kq_fd.as_raw_fd(), token);\n\n// after: use the I/O-safe borrowed registration method\nsource.register_borrowed(kq_fd.as_fd(), token);","handlingStrategy":"type-guard","validationCode":"// The panic fires when tokio calls register_borrowed and your type falls\n// through to the deprecated register() default -> unimplemented!().\n// Surface this at TEST time, not in production:\n#[test]\nfn my_source_registers_safely() {\n    let src = MySource::new(/* ... */);\n    // This drives the registration path; it panics if register_borrowed\n    // was not implemented.\n    let aio = tokio::io::bsd::Aio::new_for_aio(src)\n        .expect(\"register_borrowed must be implemented\");\n    let _ = aio;\n}\n\n// Also fail the build if anyone implements the old unsafe method:\n// in lib.rs / main.rs:\n//   #![deny(deprecated)]","typeGuard":"// Implement the SAFE method explicitly. Leaving register_borrowed to its\n// default makes it fall through to the deprecated register() -> panic.\nuse std::os::fd::BorrowedFd;\nuse tokio::io::bsd::AioSource;\n\nimpl AioSource for MySource {\n    fn register_borrowed(&mut self, kq: BorrowedFd<'_>, token: usize) {\n        // real registration using the borrowed fd...\n        let _ = (kq, token);\n    }\n    fn deregister(&mut self) {\n        // ...\n    }\n    // Deliberately do NOT override the deprecated `register`.\n}\n\n// Call-site narrowing helper: documents that the source must be safe-API ready.\n#[inline]\nfn assert_safe_aio_source<S: AioSource>(_: &S) {}","tryCatchPattern":"use std::panic::{catch_unwind, AssertUnwindSafe};\n\n// The unimplemented!() fires during Aio construction (registration). Catch it\n// so a misconfigured source degrades instead of aborting the task.\nlet res = catch_unwind(AssertUnwindSafe(|| {\n    tokio::io::bsd::Aio::new_for_aio(MySource::new(/* ... */))\n}));\nmatch res {\n    Ok(Ok(aio))  => { /* usable */ }\n    Ok(Err(io))  => { /* ordinary io::Error from registration */ }\n    Err(payload) => {\n        // register_borrowed not implemented -- fix the AioSource impl.\n        eprintln!(\"AioSource missing register_borrowed: {payload:?}\");\n    }\n}","preventionTips":["Implement `AioSource::register_borrowed` (the `BorrowedFd`-based, I/O-safe API) on every source; never rely on the deprecated raw-`RawFd` `register` method.","Compile the crate with `#![deny(deprecated)]` (or at least `-D warnings` in CI) so accidentally overriding the old `register` becomes a hard error.","Add a unit test per `AioSource` that actually constructs `Aio::new_for_aio` and/or `new_for_lio` -- the `unimplemented!()` surfaces there instead of in production.","Do not leave `register_borrowed` unimplemented hoping the default will work; the default only calls the (now-panicking) `register` for pre-1.52 back-compat.","If you must support the legacy method, override BOTH: implement `register_borrowed` for new tokio and keep a `register` shim only for older versions -- and gate the shim behind a version cfg."],"tags":["tokio","posix-aio","kqueue","bsd","deprecated","trait","io-safety"],"analyzedSha":"108d6d3dc038332af2af83957748333091e35b3f","analyzedAt":"2026-08-01T08:30:11.576Z","schemaVersion":2}