44
Send, Sync, and Compile Tests
Send and Sync are auto-traits — the compiler implements them for any type whose fields all implement them. Raw pointers do not implement either, on the assumption that an unsafe author hasn't proved thread-safety. We have to opt our list back in by writing the impls ourselves, with unsafe impl. Then we add compile-fail tests to verify we got the bounds right.
- The opt-in impls. A
LinkedList<T>isSendexactly whenT: Send(moving the list to another thread moves all the elements). It'sSyncwhenT: Sync(sharing&LinkedList<T>only exposes&T). Same impls again forIter/IterMut/IntoIter.unsafe impl<T: Send> Send for LinkedList<T> {} unsafe impl<T: Sync> Sync for LinkedList<T> {} unsafe impl<T: Send> Send for Iter<'_, T> {} unsafe impl<T: Sync> Sync for Iter<'_, T> {} unsafe impl<T: Send> Send for IterMut<'_, T> {} unsafe impl<T: Sync> Sync for IterMut<'_, T> {} unsafe impl<T: Send> Send for IntoIter<T> {} unsafe impl<T: Sync> Sync for IntoIter<T> {} - Verify the bounds with a static-assertion trick: a function with a where-clause that only compiles if the impl exists. Place these in tests; they're compile-time checks, no runtime cost. Symmetrically, set up a
trybuildtest that pins the negative cases (e.g.,LinkedList<Rc<i32>>is notSend).#[cfg(test)] fn _assert_send_sync() { fn assert_send<T: Send>() {} fn assert_sync<T: Sync>() {} assert_send::<LinkedList<i32>>(); assert_sync::<LinkedList<i32>>(); assert_send::<Iter<'_, i32>>(); assert_send::<IterMut<'_, i32>>(); assert_send::<IntoIter<i32>>(); } // trybuild test (in tests/compile-fail/not-send.rs): // fn must_be_send<T: Send>() {} // fn main() { // must_be_send::<LinkedList<std::rc::Rc<i32>>>(); // // expected: error[E0277]: ... cannot be sent between threads safely // } - Variance has the same compile-test pattern. A function that takes
LinkedList<&'static T>and returns it asLinkedList<&'a T>only compiles if the type is covariant. Add it to the test suite as a regression guard — change the marker toPhantomData<*mut Node<T>>and this assertion stops compiling.#[cfg(test)] fn _assert_covariant<'a, T>( long: LinkedList<&'static T>, ) -> LinkedList<&'a T> { long // only compiles if LinkedList is covariant in T }