< 返回版块

Kotodian 发表于 2022-12-06 14:26

Tags:PhantomData

大佬们,我看了网上很多关于PhantomData的使用都是因为unsafe rust,但我不理解tower这个项目为什么这里必须要PhantomData,我把phantomData去掉编译器也不会报错。

use std::{
    collections::VecDeque,
    fmt,
    marker::PhantomData,
    task::{Context, Poll},
};

use tower_service::Service;

pub trait Picker<S, Req> {
    fn pick(&mut self, r: &Req, services: &[S]) -> usize;
}

impl<S, F, Req> Picker<S, Req> for F
where
    F: Fn(&Req, &[S]) -> usize,
{
    fn pick(&mut self, r: &Req, services: &[S]) -> usize {
        self(r, services)
    }
}

pub struct Steer<S, F, Req> {
    router: F,
    services: Vec<S>,
    not_ready: VecDeque<usize>,
    _phantom: PhantomData<Req>,
}

impl<S, F, Req> Steer<S, F, Req> {
    pub fn new(services: impl IntoIterator<Item = S>, router: F) -> Self {
        let services: Vec<_> = services.into_iter().collect();
        let not_ready: VecDeque<_> = services.iter().enumerate().map(|(i, _)| i).collect();
        Self {
            router,
            services,
            not_ready,
            _phantom: PhantomData,
        }
    }
}

impl<S, Req, F> Service<Req> for Steer<S, F, Req>
where
    S: Service<Req>,
    F: Picker<S, Req>,
{
    type Response = S::Response;

    type Error = S::Error;

    type Future = S::Future;

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        loop {
            if self.not_ready.is_empty() {
                return Poll::Ready(Ok(()));
            } else {
                if self.services[self.not_ready[0]]
                    .poll_ready(cx)?
                    .is_pending()
                {
                    return Poll::Pending;
                }

                self.not_ready.pop_front();
            }
        }
    }

    fn call(&mut self, req: Req) -> Self::Future {
        assert!(
            self.not_ready.is_empty(),
            "Steer must wait for all services to be ready. Did you forget to call poll_ready()?"
        );

        let idx = self.router.pick(&req, &self.services[..]);
        let cl = &mut self.services[idx];
        self.not_ready.push_back(idx);
        cl.call(req)
    }
}

impl<S, F, Req> Clone for Steer<S, F, Req>
where
    S: Clone,
    F: Clone,
{
    fn clone(&self) -> Self {
        Self {
            router: self.router.clone(),
            services: self.services.clone(),
            not_ready: self.not_ready.clone(),
            _phantom: PhantomData,
        }
    }
}

impl<S, F, Req> fmt::Debug for Steer<S, F, Req>
where
    S: fmt::Debug,
    F: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let Self {
            router,
            services,
            not_ready,
            _phantom,
        } = self;
        f.debug_struct("Steer")
            .field("router", router)
            .field("services", services)
            .field("not_ready", not_ready)
            .finish()
    }
}

评论区

写评论
shuimuliang 2022-12-28 07:04

和OwnerShip有关 参考 https://doc.rust-lang.org/nomicon/phantom-data.html https://learnku.com/docs/nomicon/2018/310-phantom-data/4721

作者 Kotodian 2022-12-06 15:16

wow 非常感谢

--
👇
fakeshadow: 这个我还真不是很清楚,看上去像是要让Req类型不妨碍auto trait的生成。例如

fn main() {
    use std::marker::PhantomData;

    struct SendSync<S, Req> {
        s: S,
        req: PhantomData<Req>
    }

    fn send_sync<T: Send + Sync>() {}

    // 无法编译
    // send_sync::<SendSync<String, std::rc::Rc<()>>>();

    struct SendSync2<S, Req> {
        s: S,
        req: PhantomData<fn(Req)>
    }

    send_sync::<SendSync2<String, std::rc::Rc<()>>>();
}

--
👇
Kotodian: ``` use std::{hash::Hash, future::Future, marker::PhantomData, pin::Pin, task::{Context, Poll}};

use pin_project_lite::pin_project;

use crate::discover::Discover; use tower_service::Service; use futures_core::ready;

use super::service::Balance;

pub struct MakeBalance<S, Req> { inner: S, _marker: PhantomData<fn(Req)>, }

impl<S, Req> MakeBalance<S, Req> { pub fn new(make_discover: S) -> Self { Self { inner: make_discover, _marker: PhantomData, } } }

impl<S, Target, Req> Service for MakeBalance<S, Req> where S: Service, S::Response: Discover, <S::Response as Discover>::Key: Hash, <S::Response as Discover>::Service: Service, <<S::Response as Discover>::Service as Service>::Error: Intocrate::BoxError, { type Response = Balance<S::Response, Req>; type Error = S::Error; type Future = MakeFuture<S::Future, Req>;

fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
    self.inner.poll_ready(cx)
}

fn call(&mut self, target: Target) -> Self::Future {
    MakeFuture {
        inner: self.inner.call(target),
        _marker: PhantomData,
    }
}

}

impl<S, Req> Clone for MakeBalance<S, Req> where S: Clone, { fn clone(&self) -> Self { Self { inner: self.inner.clone(), _marker: PhantomData, } } }

impl<S, Req> std::fmt::Debug for MakeBalance<S, Req> where S: std::fmt::Debug, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self { inner, _marker } = self; f.debug_struct("MakeBalance").field("inner", inner).finish() } }

pin_project! { pub struct MakeFuture<F, Req> { #[pin] inner: F, _marker: PhantomData<fn(Req)>, } }

impl<F, T, E, Req> Future for MakeFuture<F, Req> where F: Future<Output = Result<T, E>>, T: Discover, ::Key: Hash, <::Service as Service>::Error: Intocrate::BoxError, ::Service: Service, { type Output = Result<Balance<T, Req>, E>;

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
    let this = self.project();
    let inner = ready!(this.inner.poll(cx))?;
    let svc = Balance::new(inner);
    Poll::Ready(Ok(svc))
}

}

那能问下这里为什么是用fn(Req)吗

--  
👇  
fakeshadow: 是Service trait的设计区别

// 现在 trait Service {}

// 以前 trait Service { type Request; }

以前的版本Service用关联类型来表示Request,导致泛型Req无法绑定到trait上,所以只能用phantomdata绑到impl的类型上

--  
👇  
Kotodian: 我先看下,就是想知道这是老的rust的问题吗

--  
👇  
fakeshadow: 看起来是refactor遗物.https://github.com/tower-rs/tower/pull/109





fakeshadow 2022-12-06 15:14

这个我还真不是很清楚,看上去像是要让Req类型不妨碍auto trait的生成。例如

fn main() {
    use std::marker::PhantomData;

    struct SendSync<S, Req> {
        s: S,
        req: PhantomData<Req>
    }

    fn send_sync<T: Send + Sync>() {}

    // 无法编译
    // send_sync::<SendSync<String, std::rc::Rc<()>>>();

    struct SendSync2<S, Req> {
        s: S,
        req: PhantomData<fn(Req)>
    }

    send_sync::<SendSync2<String, std::rc::Rc<()>>>();
}

--
👇
Kotodian: ``` use std::{hash::Hash, future::Future, marker::PhantomData, pin::Pin, task::{Context, Poll}};

use pin_project_lite::pin_project;

use crate::discover::Discover; use tower_service::Service; use futures_core::ready;

use super::service::Balance;

pub struct MakeBalance<S, Req> { inner: S, _marker: PhantomData<fn(Req)>, }

impl<S, Req> MakeBalance<S, Req> { pub fn new(make_discover: S) -> Self { Self { inner: make_discover, _marker: PhantomData, } } }

impl<S, Target, Req> Service for MakeBalance<S, Req> where S: Service, S::Response: Discover, <S::Response as Discover>::Key: Hash, <S::Response as Discover>::Service: Service, <<S::Response as Discover>::Service as Service>::Error: Intocrate::BoxError, { type Response = Balance<S::Response, Req>; type Error = S::Error; type Future = MakeFuture<S::Future, Req>;

fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
    self.inner.poll_ready(cx)
}

fn call(&mut self, target: Target) -> Self::Future {
    MakeFuture {
        inner: self.inner.call(target),
        _marker: PhantomData,
    }
}

}

impl<S, Req> Clone for MakeBalance<S, Req> where S: Clone, { fn clone(&self) -> Self { Self { inner: self.inner.clone(), _marker: PhantomData, } } }

impl<S, Req> std::fmt::Debug for MakeBalance<S, Req> where S: std::fmt::Debug, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self { inner, _marker } = self; f.debug_struct("MakeBalance").field("inner", inner).finish() } }

pin_project! { pub struct MakeFuture<F, Req> { #[pin] inner: F, _marker: PhantomData<fn(Req)>, } }

impl<F, T, E, Req> Future for MakeFuture<F, Req> where F: Future<Output = Result<T, E>>, T: Discover, ::Key: Hash, <::Service as Service>::Error: Intocrate::BoxError, ::Service: Service, { type Output = Result<Balance<T, Req>, E>;

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
    let this = self.project();
    let inner = ready!(this.inner.poll(cx))?;
    let svc = Balance::new(inner);
    Poll::Ready(Ok(svc))
}

}

那能问下这里为什么是用fn(Req)吗

--  
👇  
fakeshadow: 是Service trait的设计区别

// 现在 trait Service {}

// 以前 trait Service { type Request; }

以前的版本Service用关联类型来表示Request,导致泛型Req无法绑定到trait上,所以只能用phantomdata绑到impl的类型上

--  
👇  
Kotodian: 我先看下,就是想知道这是老的rust的问题吗

--  
👇  
fakeshadow: 看起来是refactor遗物.https://github.com/tower-rs/tower/pull/109




作者 Kotodian 2022-12-06 15:00
use std::{hash::Hash, future::Future, marker::PhantomData, pin::Pin, task::{Context, Poll}};

use pin_project_lite::pin_project;

use crate::discover::Discover;
use tower_service::Service;
use futures_core::ready;

use super::service::Balance;

pub struct MakeBalance<S, Req> {
    inner: S,
    _marker: PhantomData<fn(Req)>,
}

impl<S, Req> MakeBalance<S, Req> {
    pub fn new(make_discover: S) -> Self {
        Self {
            inner: make_discover,
            _marker: PhantomData,
        }
    }
}

impl<S, Target, Req> Service<Target> for MakeBalance<S, Req>
where
    S: Service<Target>,
    S::Response: Discover,
    <S::Response as Discover>::Key: Hash,
    <S::Response as Discover>::Service: Service<Req>,
    <<S::Response as Discover>::Service as Service<Req>>::Error: Into<crate::BoxError>,
{
    type Response = Balance<S::Response, Req>;
    type Error = S::Error;
    type Future = MakeFuture<S::Future, Req>;

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        self.inner.poll_ready(cx)
    }

    fn call(&mut self, target: Target) -> Self::Future {
        MakeFuture {
            inner: self.inner.call(target),
            _marker: PhantomData,
        }
    }
}

impl<S, Req> Clone for MakeBalance<S, Req>
where
    S: Clone,
{
    fn clone(&self) -> Self {
        Self {
            inner: self.inner.clone(),
            _marker: PhantomData,
        }
    }
}

impl<S, Req> std::fmt::Debug for MakeBalance<S, Req>
where
    S: std::fmt::Debug,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let Self { inner, _marker } = self;
        f.debug_struct("MakeBalance").field("inner", inner).finish()
    }
}

pin_project! {
    pub struct MakeFuture<F, Req> {
        #[pin]
        inner: F,
        _marker: PhantomData<fn(Req)>,
    }
}

impl<F, T, E, Req> Future for MakeFuture<F, Req>
where
    F: Future<Output = Result<T, E>>,
    T: Discover,
    <T as Discover>::Key: Hash,
    <<T as Discover>::Service as Service<Req>>::Error: Into<crate::BoxError>,
    <T as Discover>::Service: Service<Req>,
{
    type Output = Result<Balance<T, Req>, E>;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let this = self.project();
        let inner = ready!(this.inner.poll(cx))?;
        let svc = Balance::new(inner);
        Poll::Ready(Ok(svc))
    }
}

那能问下这里为什么是用fn(Req)吗

--
👇
fakeshadow: 是Service trait的设计区别

// 现在
trait Service<Req> {}

// 以前
trait Service {
    type Request;
}

以前的版本Service用关联类型来表示Request,导致泛型Req无法绑定到trait上,所以只能用phantomdata绑到impl的类型上

--
👇
Kotodian: 我先看下,就是想知道这是老的rust的问题吗

--
👇
fakeshadow: 看起来是refactor遗物.https://github.com/tower-rs/tower/pull/109

作者 Kotodian 2022-12-06 14:55

原来如此,谢谢

--
👇
fakeshadow: 是Service trait的设计区别

// 现在
trait Service<Req> {}

// 以前
trait Service {
    type Request;
}

以前的版本Service用关联类型来表示Request,导致泛型Req无法绑定到trait上,所以只能用phantomdata绑到impl的类型上

--
👇
Kotodian: 我先看下,就是想知道这是老的rust的问题吗

--
👇
fakeshadow: 看起来是refactor遗物.https://github.com/tower-rs/tower/pull/109

fakeshadow 2022-12-06 14:53

是Service trait的设计区别

// 现在
trait Service<Req> {}

// 以前
trait Service {
    type Request;
}

以前的版本Service用关联类型来表示Request,导致泛型Req无法绑定到trait上,所以只能用phantomdata绑到impl的类型上

--
👇
Kotodian: 我先看下,就是想知道这是老的rust的问题吗

--
👇
fakeshadow: 看起来是refactor遗物.https://github.com/tower-rs/tower/pull/109

Nayaka 2022-12-06 14:51

https://doc.rust-lang.org/nomicon/phantom-data.html#generic-parameters-and-drop-checking

作者 Kotodian 2022-12-06 14:50

我先看下,就是想知道这是老的rust的问题吗

--
👇
fakeshadow: 看起来是refactor遗物.https://github.com/tower-rs/tower/pull/109

作者 Kotodian 2022-12-06 14:49

我的意思是全去掉

--
👇
Cupnfish: 没有啊,我去掉了之后:

error[E0392]: parameter `Req` is never used
  --> src/lib.rs:23:24
   |
23 | pub struct Steer<S, F, Req> {
   |                        ^^^ unused parameter
   |
   = help: consider removing `Req`, referring to it in a field, or using a marker such as `PhantomData`
   = help: if you intended `Req` to be a const parameter, use `const Req: usize` instead

For more information about this error, try `rustc --explain E0392`.
warning: `playground` (lib) generated 1 warning
error: could not compile `playground` due to previous error; 1 warning emitted
fakeshadow 2022-12-06 14:48

看起来是refactor遗物.https://github.com/tower-rs/tower/pull/109

Cupnfish 2022-12-06 14:44

没有啊,我去掉了之后:

error[E0392]: parameter `Req` is never used
  --> src/lib.rs:23:24
   |
23 | pub struct Steer<S, F, Req> {
   |                        ^^^ unused parameter
   |
   = help: consider removing `Req`, referring to it in a field, or using a marker such as `PhantomData`
   = help: if you intended `Req` to be a const parameter, use `const Req: usize` instead

For more information about this error, try `rustc --explain E0392`.
warning: `playground` (lib) generated 1 warning
error: could not compile `playground` due to previous error; 1 warning emitted
1 共 11 条评论, 1 页