futures::lock::Mutex is not implementing RefUnwindSafe trait (see https://docs.rs/futures/0.3.17/futures/lock/struct.Mutex.html#impl-RefUnwindSafe )
Why is not safe to pass a futures::lock::Mutex inside std::panic::catch_unwind?
Reproducible code:
use std::panic::catch_unwind;
use futures::lock::Mutex;
fn main() {
let m = Mutex::new(String::new());
catch_unwind(|| {
m.lock()
});
}
Could futures::lock::Mutex implement the RefUnwindSafe trait ?
The documentation for the UnwindSafe trait hints at a reason for this:
Who implements UnwindSafe?
Types such as &mut T and &RefCell are examples which are not unwind safe. The general idea is that any mutable state which can be shared across catch_unwind is not unwind safe by default. This is because it is very easy to witness a broken invariant outside of catch_unwind as the data is simply accessed as usual.
Types like &Mutex, however, are unwind safe because they implement poisoning by default. They still allow witnessing a broken invariant, but they already provide their own “speed bumps” to do so.
futures::lock::Mutex provides mutability like &mut T and RefCell<T>, but does not implement the poisoning feature of std::sync::Mutex<T>, so it does not implement UnwindSafe.
Though as the documentation points out, the UnwindSafe is less about memory safety and more about upholding logical invariants - hence why neither UnwindSafe nor AssertUnwindSafe are unsafe.
Related
You can do this:
impl Foo {
fn foo(self: &Rc<Self>) {}
}
But not this:
impl Foo {
fn foo(self: &Rc<RefCell<Self>>) {}
}
The former is quite useful - e.g. I can have methods return objects containing weak references to self. But because I can't use RefCell I can't return anything that would mutate self.
There are ways around this (e.g. wrapping the whole struct in RefCell internally) but none as convenient for my current task as just allowing self: &Rc<RefCell<>>.
The grammar allowed is described here. It allows Box, Rc, Arc and Pin but not RefCell. Why?
As of the time of this writing, in August 2022, method receiver type support is still fairly limited to a handful of types, and compositions of those types. You've noticed that RefCell is not among them.
arbitrary_self_types, a feature for expanding the possible types of self, has a tracking issue for discussing its implementation. From the discussion, it seems that this feature is currently targeting types that implement Deref[Mut], which RefCell does not implement. A few comments point out that this is limiting though, so there's still a possibility.
Ultimately, I think the answer is that RefCell does not work because the full-fledged feature hasn't been designed thoroughly enough yet. The Rust team likely implemented the basic feature so that we could have Pin as a self type and thus make futures work, and added a few other "easy" types to get more bang for their buck, but a proper and generic implementation has been deferred.
I often use the newtype pattern, but I am tired of writing my_type.0.call_to_whatever(...). I am tempted to implement the Deref trait because it permits writing simpler code since I can use my newtype as if it were the underlying type in some situations, e.g.:
use std::ops::Deref;
type Underlying = [i32; 256];
struct MyArray(Underlying);
impl Deref for MyArray {
type Target = Underlying;
fn deref(&self) -> &Self::Target {
&self.0
}
}
fn main() {
let my_array = MyArray([0; 256]);
println!("{}", my_array[0]); // I can use my_array just like a regular array
}
Is this a good or bad practice? Why? What can be the downsides?
the rules regarding Deref and DerefMut were designed specifically to accommodate smart pointers. Because of this, Deref should only be implemented for smart pointers to avoid confusion.
— std::ops::Deref
I think it's a bad practice.
since I can use my newtype as if it were the underlying type in some situations
That's the problem — it can be implicitly used as the underlying type whenever a reference is. If you implement DerefMut, then it also applies when a mutable reference is needed.
You don't have any control over what is and what is not available from the underlying type; everything is. In your example, do you want to allow people to call as_ptr? What about sort? I sure hope you do, because they can!
About all you can do is attempt to overwrite methods, but they still have to exist:
impl MyArray {
fn as_ptr(&self) -> *const i32 {
panic!("No, you don't!")
}
}
Even then, they can still be called explicitly (<[i32]>::as_ptr(&*my_array);).
I consider it bad practice for the same reason I believe that using inheritance for code reuse is bad practice. In your example, you are essentially inheriting from an array. I'd never write something like the following Ruby:
class MyArray < Array
# ...
end
This comes back to the is-a and has-a concepts from object-oriented modeling. Is MyArray an array? Should it be able to be used anywhere an array can? Does it have preconditions that the object should uphold that a consumer shouldn't be able to break?
but I am tired of writing my_type.0.call_to_whatever(...)
Like in other languages, I believe the correct solution is composition over inheritance. If you need to forward a call, create a method on the newtype:
impl MyArray {
fn call_to_whatever(&self) { self.0.call_to_whatever() }
}
The main thing that makes this painful in Rust is the lack of delegation. A hypothetical delegation syntax could be something like
impl MyArray {
delegate call_to_whatever -> self.0;
}
While waiting for first-class delegation, we can use crates like delegate or ambassador to help fill in some of the gaps.
So when should you use Deref / DerefMut? I'd advocate that the only time it makes sense is when you are implementing a smart pointer.
Speaking practically, I do use Deref / DerefMut for newtypes that are not exposed publicly on projects where I am the sole or majority contributor. This is because I trust myself and have good knowledge of what I mean. If delegation syntax existed, I wouldn't.
Contrary to the accepted answer, I found out that some popular crates implement Deref for types which are newtypes and aren't smart pointers:
actix_web::web::Json<T> is a tuple struct of (T,) and it implements Deref<Target=T>.
bstr::BString has one field typed Vec<u8> and it implements Deref<Target=Vec<u8>>.
So, maybe it's fine as long as it's not abused, e.g. to simulate multi-level inheritance hierarchies. I also noticed that the two examples above have either zero public methods or only one into_inner method which returns the inner value. It seems then a good idea to keep the number of methods of a wrapper type minimal.
I'm working on a library that operates on [T] slices. I would like the library user to specify a type that can retrieve a slice in a mutable or immutable way.
My current lib defines 2 traits
pub trait SliceWrapper<T> {
fn slice(&self) -> &[T];
}
pub trait SliceWrapperMut<T> {
fn slice_mut (&mut self) -> &mut [T];
}
But the names slice and slice_mut seem arbitrary and not somewhere in the core Rust libs.
Is there a trait I should be requiring instead, like Into::<&mut [T]> ?
I'm afraid that Into consumes the type rather than just referencing it, so I can't simply demand the caller type implements core::convert::Into, can I?
The simplest way to answer this is to look at the documentation for existing types that more or less do what you want. This sounds like something Vec would do, so why not look at the documentation for Vec?
If you search through it looking for -> &, you'll find numerous methods that return borrowed slices:
Vec::as_slice
Borrow::borrow
Index::<Range<usize>>::index (plus for RangeTo<usize>, RangeFrom<usize>, RangeFull, RangeInclusive<usize>, and RangeToInclusive<usize>)
Deref::deref
AsRef::<[T]>::as_ref
Which of these should you implement? I don't know; which ones make sense? Read the documentation on each method (and the associated trait) and see if what it describes is what you want to permit. You say "that can retrieve a slice", but you don't really explain what that means. A big part of traits is not just abstracting out common interfaces, but giving those interfaces meaning beyond what the code strictly allows.
If the methods listed aren't right; if none of them quite convey the correct semantics, then make a new trait. Don't feel compelled to implement a trait just because you technically can.
As for Into, again, read the documentation:
A conversion that consumes self, which may or may not be expensive.
Emphasis mine. Implementing Into in this context makes no sense: if you consume the value, you can't borrow from it. Oh, also:
Library authors should not directly implement this trait, but should prefer implementing the From trait, which offers greater flexibility and provides an equivalent Into implementation for free, thanks to a blanket implementation in the standard library.
So yeah, I wouldn't use Into for this. Or From.
std::sync::atomic::AtomicUsize implements Sync which means immutable references are free of data races when shared between multiple threads. Why does AtomicUsize not implement Send? Is there state which is linked to the thread that created the atomic or is this a language design decision relating to the way atomics are intended to be used i.e. via a Arc<_> etc.
It's a trick! AtomicUsize does implement Send:
use std::sync::atomic::AtomicUsize;
fn checker<T>(_: T) where T: Send {}
fn main() {
checker(AtomicUsize::default());
}
In fact, there's even an automated test that ensures this is the case.
Rust 1.26
These auto traits are now documented, thanks to a change made to rustdoc.
Previous versions
The gotcha lies in how Send is implemented:
This trait is automatically derived when the compiler determines it's appropriate.
This means that Rustdoc doesn't know that Send is implemented for a type because most types don't implement it explicitly.
This explains why AtomicPtr<T> shows up in the implementers list: it has a special implementation that ignores the type of T.
Saying the same thing in code, when would I pick either of the following examples?
unsafe trait MyCoolTrait {
fn method(&self) -> u8;
}
trait MyCoolTrait {
unsafe fn method(&self) -> u8;
}
The opt-in builtin traits (OIBIT) RFC states:
An unsafe trait is a trait that is unsafe to implement, because it represents some kind of trusted assertion. Note that unsafe traits are perfectly safe to use. Send and Share (note: now called Sync) are examples of unsafe traits: implementing these traits is effectively an assertion that your type is safe for threading.
There's another example of an unsafe trait in the standard library, Searcher. It says:
The trait is marked unsafe because the indices returned by the next() methods are required to lie on valid utf8 boundaries in the haystack. This enables consumers of this trait to slice the haystack without additional runtime checks.
Unfortunately, neither of these paragraphs really help my understanding of when it is correct to mark the entire trait unsafe instead of some or all of the methods.
I've asked about marking a function as unsafe before, but this seems different.
A function is marked unsafe to indicate that it is possible to violate memory safety by calling it. A trait is marked unsafe to indicate that it is possible to violate memory safety by implementing it at all. This is commonly because the trait has invariants that other unsafe code relies on being upheld, and that these invariants cannot be expressed any other way.
In the case of Searcher, the methods themselves should be safe to call. That is, users should not have to worry about whether or not they're using a Searcher correctly; the interface contract says all calls are safe. There's nothing you can do that will cause the methods to violate memory safety.
However, unsafe code will be calling the methods of a Searcher, and such unsafe code will be relying on a given Searcher implementation to return offsets that are on valid UTF-8 code point boundaries. If this assumption is violated, then the unsafe code could end up causing a memory safety violation itself.
To put it another way: the correctness of unsafe code using Searchers depends on every single Searcher implementation also being correct. Or: implementing this trait incorrectly allows for safe code to induce a memory safety violation is unrelated unsafe code.
So why not just mark the methods unsafe? Because they aren't unsafe at all! They don't do anything that could violate memory safety in and of themselves. next_match just scans for and returns an Option<(usize, usize)>. The danger only exists when unsafe code assumes that these usizes are valid indices into the string being searched.
So why not just check the result? Because that'd be slower. The searching code wants to be fast, which means it wants to avoid redundant checks. But those checks can't be expressed in the Searcher interface... so instead, the whole trait is flagged as unsafe to warn anyone implementing it that there are extra conditions not stated or enforced in the code that must be respected.
There's also Send and Sync: implementing those when you shouldn't violates the expectations of (among other things) code that has to deal with threads. The code that lets you create threads is safe, but only so long as Send and Sync are only implemented on types for which they're appropriate.
The rule of thumb will be like this:
Use unsafe fn method() if the method user need to wrap method call in unsafe block.
Use unsafe trait MyTrait if the trait implementor need to unsafe impl MyTrait.
unsafe is a tip to Rust user: unsafe code must be written carefully.
The key point is that unsafe should be used as dual: when an author declare a trait/function as unsafe, the implementor/user need to implement/use it with unsafe.
When function is marked as unsafe, it means the user needs to use the function carefully. The function author is making assumption that the function user must keep.
When trait is marked as unsafe, it means the trait implementor needs to implement carefully. The trait requires implementor keeps certain assumption. But users of the unsafe trait can insouciantly call methods defined in the trait.
For concrete example, unsafe trait Searcher requires all Searcher implementation should return valid utf8 boundary when calling next. And all implementation are marked as unsafe impl Searcher, indicating implementation code might be unsafe. But as a user of Searcher, one can call searcher.next() without wrapping it in unsafe block.