Many traits were used in the code, resulting in compilation exceptions such as the need to add Box, dyn, etc

Many traits were used in the code, resulting in compilation exceptions such as the need to add Box, dyn, etc.
For example, what appears now:


use crate::transport::config::ClientConfig;
use crate::transport::session::{ClientSession, Session};


pub enum ClientModel {
    Tcp
}

impl ClientModel {
    pub fn to_client(&self) -> impl Client {
        match self {
            ClientModel::Tcp => TcpClient::new()
        }
    }
}

pub trait Client {
    fn open(&self);
}

#[derive(Debug, Clone, Default)]
pub struct TcpClient {
    config:ClientConfig,
    session: Box<dyn ClientSession>,
}

impl TcpClient {
    pub fn new() -> TcpClient {
        TcpClient{
            config: ClientConfig::new(true),
            session: Box::new(Session::default()),
        }
    }
}

impl Client for TcpClient {
    fn open(&self)  {
        println!("open!!!")
    }
}

impl ClientConnector for TcpClient {

}

unsafe impl Sync for TcpClient {

}



pub trait ClientConnector{

}
error[E0038]: the trait `ClientSession` cannot be made into an object
  --> src\transport\client.rs:25:18
   |
25 |     session: Box<dyn ClientSession>,
   |                  ^^^^^^^^^^^^^^^^^ `ClientSession` cannot be made into an object
   |
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
  --> src\transport\session.rs:6:34
   |
6  | pub trait ClientSession: Debug + Clone{
   |           -------------          ^^^^^ ...because it requires `Self: Sized`
   |           |
   |           this trait cannot be made into an object...


May I ask why this is and how I need to solve it

this trait is not object safe, this means you cannot use dyn ClientSession anywhere, whether it's a reference or box. you can only use generic types with the trait as bound. or, in your example, I see you only need the actual Session type, so you don't even need a generic type, I don't know why you use the dyn ClientSession in the first place, just replace dyn ClientSession with Session should do it.

pub struct TcpClient {
    config:ClientConfig,
    session: Session,
}

impl TcpClient {
    pub fn new() -> TcpClient {
        TcpClient{
            config: ClientConfig::new(true),
            session: Session::default(),
        }
    }
}

I have no idea what the trait is supposed to do. maybe if you describe your problem in detail, or show more code, especially the definition of the ClientSession trait, people can give you better suggestions.

1 Like

Thank you for your reply. Your answer gave me great inspiration. I solved this problem using generics. I did this because I originally had a socket protocol and wanted to refactor it using Rust

I encountered other exceptions when using generics

use std::collections::HashMap;
use std::fmt::{Debug, Formatter};
use std::iter::Map;

/**
心跳处理器
 */
pub trait HeartbeatHandler {
    fn heartbeat();
}


/**
 * 指令流枚举
 */
#[derive(Debug,Eq, PartialEq)]
pub enum Flag {
    /**
     * 未知
     */
    Unknown = 0,
    /**
     * 链接
     */
    Connect = 10,
    //握手:连接(c->s),提交客户端握手信息,请求服务端握手信息
    /**
     * 链接确认
     */
    Connack = 11,
    //握手:确认(c<-s),响应服务端握手信息
    /**
     * Ping
     */
    Ping = 20,
    //心跳:ping(c<->s)
    /**
     * Pong
     */
    Pong = 21,
    //心跳:pong(c<->s)
    /**
     * 关闭(Udp 没有断链的概念,需要发消息)
     */
    Close = 30,
    /**
     * 告警
     */
    Alarm = 31,
    /**
     * 消息
     */
    Message = 40,
    //消息(c<->s)
    /**
     * 请求
     */
    Request = 41,
    //请求(c<->s)
    /**
     * 订阅
     */
    Subscribe = 42,
    /**
     * 回复
     */
    Reply = 48,
    /**
     * 回复结束(结束订阅接收)
     */
    ReplyEnd = 49,
}

pub trait MessageInternal {
    /**
    获取消息指令流
     */
    fn flag(&self) -> &Flag;
}

/**
 * 消息
 *
 * @author hans
 * @since 2.0
 */
pub trait Message {
    /**
     *  是否为请求
     */
    fn is_request(&self) -> bool;

    /**
     *  是否为订阅
     */
    fn is_subscribe(&self) -> bool;
}

/**
 * 帧(帧[消息[实体]])
 *
 * @author hans
 * @since 2.0
 */
#[derive(Debug)]
pub struct Frame<T>
where T:MessageInternal+Message
{
    flag: Flag,
    message: T,
}

impl<T> Frame<T> {
    pub fn new(flag: Flag, message: (impl MessageInternal + Message)) -> Frame<T>
    {
        Frame {
            flag,
            message,
        }
    }
}

/**
 * 消息实体(帧[消息[实体]])
 *
 * @author hans
 * @since 2.0
 */
pub trait Entity:Debug {
    /**
     * at
     */
    fn at(&mut self) -> &Option<&String> {
        &self.meta("@")
    }

    /**
     * 获取元信息字典
     */
    fn meta_map(&mut self) -> &HashMap<String, String>;

    /**
     * 获取元信息
     */
    fn meta(&mut self, name: &str) -> &Option<&String>;
}

#[derive(Debug)]
pub struct EntityDefault {
    meta_map: HashMap<String, String>,
    meta_string_changed: bool,
    meta_string: String,
}

impl Entity for EntityDefault {
    fn meta_map(&mut self) -> &HashMap<String, String> {
        if self.meta_map.is_empty() {
            self.meta_map = HashMap::new();
            self.meta_string_changed = false;


            if !self.meta_string.is_empty() {
                &self.meta_string.split("&").for_each(|x| {
                    let idx = x.find('=').unwrap_or_else(|| 0);
                    if idx > 0 {
                        let str=x.to_string();
                        self.meta_map.insert(str[0..idx].to_string(), str[idx + 1..].to_string());
                    }
                });
            }
        }
        &self.meta_map
    }

    fn meta(&mut self, name: &str) -> &Option<&String> {
        &self.meta_map.get(name)
    }
}


#[derive(Debug)]
pub struct MessageDefault<T>
where T:Entity+Debug
{
    sid: String,
    event: String,
    entity: Option<T>,
    flag: Flag,
}

impl<T> Default for MessageDefault<T> {
    fn default() -> Self {
        MessageDefault {
            sid: "".to_string(),
            event: "".to_string(),
            entity: None,
            flag: Flag::Unknown,
        }
    }
}

impl<T> MessageInternal for MessageDefault<T> {
    fn flag(&self) -> &Flag {
        &self.flag
    }
}

impl<T> Message for MessageDefault<T> {
    fn is_request(&self) -> bool {
        self.flag.eq(&Flag::Request)
    }

    fn is_subscribe(&self) -> bool {
        self.flag.eq(&Flag::Subscribe)
    }
}

error[E0277]: the trait bound `T: MessageInternal` is not satisfied
   --> src\transport\core.rs:113:9
    |
113 | impl<T> Frame<T> {
    |         ^^^^^^^^ the trait `MessageInternal` is not implemented for `T`
    |
note: required by a bound in `Frame`
   --> src\transport\core.rs:107:9
    |
106 | pub struct Frame<T>
    |            ----- required by a bound in this struct
107 | where T:MessageInternal+Message
    |         ^^^^^^^^^^^^^^^ required by this bound in `Frame`
help: consider restricting type parameter `T`
    |
113 | impl<T: transport::core::MessageInternal> Frame<T> {
    |       ++++++++++++++++++++++++++++++++++


this is not how you use generic. the message field of the struct Frame has the type T, but your function takes some other (unnamed) generic type that has the bounds MessageInternal + Message. you should use T for the function parameter, and also add the trait bounds to T:

impl<T> Frame<T>
where
   T: MessageInternal + Message
{
    pub fn new(flag: Flag, message: T) -> Self
    {
        Frame {
            flag,
            message,
        }
    }
}

for other similar compile errors, first try apply the fix suggested by the compiler, for example:

4 Likes

Thank you very much. I completed it before seeing your message, but this approach seems to be incorrect when there are multiple trait fields in my structure


#[derive(Debug)]
pub struct TcpClient<T>
    where T: ClientSession
{
    config: ClientConfig,
    connector: impl ClientConnector,
    session: T,
}

impl<T: ClientSession+Default> TcpClient<T> {
    pub fn new_default() -> TcpClient<T> {
        TcpClient {
            config: ClientConfig::default(),
            connector: TcpDefaultClientConnector::default(),
            session: T::default(),
        }
    }
}

there's no such thing as "trait field", a field has a "type", and traits are not types. it's just the type of the field can be generic.

if you need multiple generic types, just declare multiple of them (unless it's very obvious, it's generally better to give the generic type meaningful names, especially when there's multple generic types):

pub struct TcpClient<Session, Connector>
where
    Session: ClientSession,
    Connector: ClientConnector,
{
    config: ClientConfig,
    connector: Connector,
    session: Session,
}

again, if you are only creating a TcpDefaultClientConnector, why bother with generics instead of just use the concrete type? are you translating some code from other OO languages (e.g. java, C#, etc.)? if you don't need it, don't add complexity to your design, especially when you are not experienced with the language yet.

I have looked up some information and chose to abandon generics and use abstract traits. I chose BOX, but I don't know. Now that I have compiled it, I would like to know if the operation selection for BOXis appropriate, and if it has any defects or differences

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.