use valrow::*; use abistr::{cstr, CStrPtr, CStrNonNull}; use core::cell::*; use core::mem::size_of; use core::ptr::NonNull; // Example usage of Wrapper fn main() { let mut channels = CHANNELS.lock().unwrap(); channels.add(cstr!("#gamedev")); channels.add(cstr!("#rust")); let mut users = USERS.lock().unwrap(); users.add(cstr!("MaulingMonkey")); // allows use of `channels` within ZST-requiring `users.for_each` let mut channels_mut = ValrowMut::new(&mut *channels); users.for_each(move |user| { let user = user.to_string_lossy(); print!("PRIVMSG {user} :Check out the following channels!"); channels_mut.for_each(move |channel| { let channel = channel.to_string_lossy(); print!(" {channel}"); #[cfg(xxx)] // won't compile: accessing `user` would make this closure a !ZST println!("PRIVMSG {user} :{channel}"); // we could use a `thread_local! { static CURRENT_CHANNEL : ... = ...; }` instead? }); println!(); }); // `channels_mut` borrow has ended, `channels` is once more accessible channels.for_each(move |channel| print!(" {}", channel.to_string_lossy())); } // Implementation of Wrapper use sealed::*; mod sealed { // make some single-instance ZSTs with an inaccessible `.0` field to act as marker types use std::sync::*; #[doc = "C++'s global, channels"] pub struct Channels(()); #[doc = "C++'s global, users" ] pub struct Users(()); pub static CHANNELS : Mutex = Mutex::new(Channels(()) ); pub static USERS : Mutex = Mutex::new(Users(()) ); unsafe impl valrow::Borrowable for Channels { type Abi = (); } unsafe impl valrow::Borrowable for Users { type Abi = (); } } impl Channels { pub fn add(&mut self, channel: CStrNonNull) { extern "C" { fn add_channel(channel: CStrNonNull); } unsafe { add_channel(channel) } } pub fn for_each(&self, mut per_channel: PerChannel) { extern "C" { fn for_each_channel(per_channel: extern "C" fn(channel: CStrPtr)); } unsafe { for_each_channel(adapt::) } let _ = per_channel; let _ = StaticAssert::::IS_ZST; extern "C" fn adapt(channel: CStrPtr) { (unsafe { NonNull::::dangling().as_mut() })(channel) } } } impl Users { pub fn add(&mut self, user: CStrNonNull) { extern "C" { fn add_user(user: CStrNonNull); } unsafe { add_user(user) } } pub fn for_each(&self, mut per_user: PerUser) { extern "C" { fn for_each_user(per_user: extern "C" fn(user: CStrPtr)); } unsafe { for_each_user(adapt::) }; let _ = per_user; let _ = StaticAssert::::IS_ZST; extern "C" fn adapt(user: CStrPtr) { (unsafe { NonNull::::dangling().as_mut() })(user) } } } /// ### Safety /// Do not call this unless a ZST exists that you should have exclusive access to. /// Note that the reference lifetime is unfortunately unbounded. unsafe fn zst_mut<'zst, ZST>() -> &'zst mut ZST { let _ = StaticAssert::::IS_ZST; unsafe { NonNull::::dangling().as_mut() } } struct StaticAssert(T); impl StaticAssert { const IS_ZST : () = assert!(0 == size_of::(), "T is not a ZST"); }