当前位置:网站首页>Pin details in rust

Pin details in rust

2022-06-27 07:53:00 51CTO

Relevant concepts

​Pin<P<T>>​

This is a struct, What it does is it takes P The point is T Fixed in memory , Can't move . To put it bluntly , Just can't pass safe The code gets ​&mut T​​.

Pin<P> The definition is as follows :


      
      
pub struct Pin<P> {
pointer: P,
}
  • 1.
  • 2.
  • 3.


Unpin

This is a trait, It's defined in std::marker in , If one T: Unpin, Just explain T stay pin It can move safely , Actually, you can get ​&mut T​​.


      
      
pub auto trait Unpin {}
  • 1.


!Unpin

Yes Unpin Take the opposite ,!Unpin The double negation of is pin. If a type contains PhantomPinned, So this type is !Unpin.


      
      
pub struct PhantomPinned;


#[stable(feature = "pin", since = "1.33.0")]
impl !Unpin for PhantomPinned {}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.


​Pin<P>​ The implementation of the

We're just going to focus here safe Method , The key is new Method :

      
      
impl<P: Deref<Target: Unpin>> Pin<P> {
pub fn new(pointer: P) -> Pin<P> {
unsafe { Pin::new_unchecked(pointer) }
}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.


It can be seen that , Only P The point is T: Unpin, Can only be new a Pin<P<T>>. there T Should be pin Example , But because of T: Unpin actually T Instances of are not pin. in other words ,T It didn't come true Unpin trait when ,T Will be really pin live .

because Pin::new Method requirement T: Unpin, Usually creating one does not support Unpin Of T Of pin The method of the example is to use Box::pin Method , The definition is as follows :

      
      
pub fn pin(x: T) -> Pin<Box<T>> {
(box x).into()
}
  • 1.
  • 2.
  • 3.


for example , The custom Node structure , The following code is generated pin example :

      
      
let node_pined: Pin<Box<Node>> = Box::pin(Node::new());
let movded_node_pined = node_pined;
  • 1.
  • 2.


Node It didn't come true Unpin when , adopt Pin You can't get any safe methods &mut Node, So you can't move Node example . Be careful , This is immovable Node example ,node_pined yes Pin example , It can be moved .

Of course , adopt Pin Of unsafe Method , You can still get mut Node, It can also be moved Node example , But these unsafe The operation of requires the programmer to take the risk .Pin This is described in detail in the relevant methods .

Pin Can be thought of as a limit pointer (​Box<T>​​ or ​​&mut T​​) Structure , stay ​​T: Unpin​​ Under the circumstances ,​​Pin<Box<T>>​​ and ​​Box<T>​​ It's similar , adopt ​​DerefMut​​ You can get ​​&mut T​​, stay T It didn't come true Unpin Under the circumstances ,​​Pin<Box<T>>​​ Only through ​​Deref​​ obtain ​​&T​​, That is to say T By pin Live in the .

Pin This method of discarding martial arts is strange , Why would there be Pin? although Box、Rc、Arc Equal pointer types can also make instances in heap Middle fixation , But these pointers are safe The method will expose &mut T, This will lead to T Instances of are moved , Such as through std::mem::swap Method , It can also be Option::take Method , It could be Vec::set_lenVec::resize Such method , These are safe Other methods . What these methods have in common is the need &mut Self, So as long as it is not exposed &mut Self, You can achieve pin The goal of .

Why pin?

Here's how it started Async/.Await The need for asynchronous programming .

Take a look at the following asynchronous programming code :

      
      
let fut_one = /* ... */;
let fut_two = /* ... */;
async move {
...
fut_one.await;
...
fut_two.await;
...
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.


rustc The following code will be generated automatically during compilation , Among them AsyncFuture It will be a self referencing structure :

      
      
// The `Future` type generated by our `async { ... }` block
struct AsyncFuture {
...
fut_one: FutOne,
fut_two: FutTwo,
state: State,
}


// List of states our `async` block can be in
enum State {
AwaitingFutOne,
AwaitingFutTwo,
Done,
}


impl Future for AsyncFuture {
type Output = ();


fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
...
}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.


Be careful Future::poll The first parameter of the method is Pin<&mut Self>, If in Future::poll Methods are similar std::mem::swap And so on , It may lead to AsyncFuture Be moved , that AsyncFuture Self reference in field It will lead to disaster .

Maybe you've noticed that , there Future::poll The code is automatically generated , You don't have to call std::mem::swap Other methods , It won't lead to AsyncFuture Be moved . It's true , If here will Future::poll The first parameter of is changed to Box<Self> perhaps &mut Self, The probability is no problem . quite a lot executor The implementation of the , It's all about requirements Future It's supporting Unpin, Because in poll There are changes in the code Self The needs of , But no errors will occur , That's why .

however , For programmer implementation Future The situation of , Here's the problem .** If poll The parameter is &mut Self, Then programmers may use safe Code ( such as std::mem::swap) Make a mistake , This is the rust The idea of secure coding conflicts with .** This is it. Pin The root cause of the introduction !

Actually , stay future 0.1 In the version ,poll The parameter of is &mut Self, as follows :

      
      
pub trait Future {
type Item;
type Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error>;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.


To sum up

  • Pin The actual is P Pointer restrictions , stay T It didn't come true Unpin Under the circumstances , avoid P Pointer exposure &mut Self.
  • Pin The introduction of Async/.Await The need for asynchronous programming , The core is Future::poll Method parameters are required .
  • except Future::poll Out of the way , Not recommended Pin, There is no need to use Pin.


原网站

版权声明
本文为[51CTO]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/178/202206270632017860.html