I have troubles understanding why this code doesn't compile:
use std::cell::{Ref, RefCell};
struct St {
data: RefCell<uint>
}
impl St {
pub fn test(&self) -> Ref<uint> {
self.data.borrow()
}
}
// This code would compile without T constrained to be Send.
fn func<T: Send>(_: &T) {
}
fn main() {
let s = St { data: RefCell::new(42) };
{
let r7 = s.test();
// Do not compile
func(&r7)
}
// Compile
func(&s);
}
It gives the following error:
bug.rs:21:18: 21:19 error: `s` does not live long enough
bug.rs:21 let r7 = s.test();
^
note: reference must be valid for the static lifetime...
bug.rs:17:11: 28:2 note: ...but borrowed value is only valid for the block at 17:10
bug.rs:17 fn main() {
bug.rs:18 let s = St { data: RefCell::new(42) };
bug.rs:19
bug.rs:20 {
bug.rs:21 let r7 = s.test();
bug.rs:22 // Do not compile
...
The issue seems to be in the function func()
when I try to constrain T
to be compatible with the Send
trait. Without this constraint this code compiles without error.
Does anybody can explain to me what is the reason of this behavior?
Send means that a type is safe to move from one thread to another. If the same type also implements Copy , this also means that it is safe to copy from one thread to another. Sync means that a type is safe to reference from multiple threads at the same time.
A static is never "inlined" at the usage site, and all references to it refer to the same memory location. Static items have the static lifetime, which outlives all other lifetimes in a Rust program. Static items may be placed in read-only memory if the type is not interior mutable.
Rust captures this through the Send and Sync traits. A type is Send if it is safe to send it to another thread. A type is Sync if it is safe to share between threads (T is Sync if and only if &T is Send).
Update for Rust 1.0
In Rust 1.0 and later the code in the example (when uint
s are replaced with some existing type) fails with another error:
% rustc test.rs
test.rs:23:9: 23:13 error: the trait `core::marker::Sync` is not implemented for the type `core::cell::UnsafeCell<usize>` [E0277]
test.rs:23 func(&r7)
^~~~
test.rs:23:9: 23:13 help: run `rustc --explain E0277` to see a detailed explanation
test.rs:23:9: 23:13 note: `core::cell::UnsafeCell<usize>` cannot be shared between threads safely
test.rs:23:9: 23:13 note: required because it appears within the type `core::cell::Cell<usize>`
test.rs:23:9: 23:13 note: required because it appears within the type `core::cell::BorrowRef<'_>`
test.rs:23:9: 23:13 note: required because it appears within the type `core::cell::Ref<'_, i32>`
test.rs:23:9: 23:13 note: required by `func`
This is kinda tricky - another trait, Sync
, has appeared out of nowhere.
A type implementing Send
trait (though its documentation is certainly lacking as of now) is something which can be transferred across task boundaries. Most of types are Send
, but some, like Rc
and Weak
, are not Send
because instances of such types may share non-synchronized mutable state and therefore are unsafe to use from multiple threads.
In older Rust versions Send
implied 'static
, so references were not Send
. Since Rust 1.0, however, Send
no longer implies 'static
, therefore references can be sent across threads. However, in order for &T
to be Send
, T
must be Sync
: this is required by the following implementation:
impl<'a, T> Send for &'a T where T: Sync + ?Sized
But in our case we're not requiring that &T
is Send
, we only require that T
is Send
, so it shouldn't really matter, right?
No. In fact, there still are references, even we don't see them right away. Remember, for a type to be Send
each its component must be Send
, that is, each field of a struct and each part of each enum variant of an enum must be Send
for this struct/enum to be Send
as well. core::cell::Ref
internally contains an instance of struct BorrowRef
, which in turn contains a reference to Cell<BorrowFlag>
. And here is where Sync
comes from: in order or &Cell<BorrowFlag>
to be Send
, Cell<BorrowFlag>
must be Sync
; however, it is not and can not be Sync
because it provides unsynchronized internal mutability. This is the actual cause of the error.
According to the Rust reference (emphasis mine) :
Send
: Types of this kind can be safely sent between tasks. This kind includes scalars, boxes, procs, and structural types containing only other owned types. All Send types are'static
.
Indeed, if you send something to an other task, you must guarantee that it won't be destroyed before this other task has finished using it, so it can't be owned by the current task.
There are two ways of ensuring it :
So by requiring that the argument of your function to be Send
, you require r7
to be 'static
, but it cannot outlive s
(as it's a reference to the RefCell contents), which isn't 'static
as it's defined in your main.
More generally, when writing
fn foo<T: 'a>(bar: T);
You require T
to be either :
'a
or longer (or having no arguments)&'a
reference to a type itself being 'a
(and you can recurse on these conditions)And as we saw, T: Send
implies T: 'static
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With