Generics 为什么trait中的泛型方法需要调整trait对象的大小?

Generics 为什么trait中的泛型方法需要调整trait对象的大小?,generics,rust,Generics,Rust,我有以下代码(): 我发现在这种情况下,我必须要求大小,但这并不能解决问题。如果我将我的send_embed方法更改为以下内容: fn send_embed<F: FnOnce(String) -> String>(&self, u64, &str, f: F) -> Option<u64> where Self: Sized + Sync + Send; 给出: error[E0277]:特性绑定的'Messenger+'stati

我有以下代码():

我发现在这种情况下,我必须要求
大小
,但这并不能解决问题。如果我将我的
send_embed
方法更改为以下内容:

fn send_embed<F: FnOnce(String) -> String>(&self, u64, &str, f: F)
    -> Option<u64> where Self: Sized + Sync + Send;
给出:

error[E0277]:特性绑定的'Messenger+'static:std::marker::Sized'不满足
--> :37:17
|
37 | b.messenger.send|u嵌入(0u64,“ABRACADABRA”,s | s);
|^^^^^^^^^^特性“std::marker::Sized”未为“Messenger+”静态应用程序实现`
|
=注意:`Messenger+'static`在编译时没有已知的常量大小
我完全被困在这里了。不知道如何在特征中使用多态性和泛型方法。有什么办法吗?

动态分派(即通过trait对象调用方法)通过vtable(即使用函数指针)进行调用,因为您在编译时不知道它将是哪个函数

但如果您的函数是泛型的,则需要对实际使用的
F
的每个实例进行不同的编译(单态)。这意味着您将有一个不同的
send\u embed
副本,用于调用它的每个不同的闭包类型。每个闭包都是不同的类型

这两个模型是不兼容的:您不能有一个可用于不同类型的函数指针

但是,您可以将该方法更改为同时使用trait对象,而不是编译时泛型:

pub trait Messenger : Sync + Send {
    fn send_embed(&self, u64, &str, f: &Fn(String) -> String)
        -> Option<u64> where Self: Sync + Send;
}

我已经更新了游乐场,添加了一个使用引用闭包直接调用
send\u embed
的示例,以及通过
Bot

上的通用包装器的间接路由。由于无法使用它实现vtable,因此无法创建通用方法。详细解释了原因

在您的例子中,您可以通过使
f
获取一个trait对象而不是泛型参数,使
send\u嵌入
对象trait。如果您的函数接受一个
f:f,其中f:Fn(X)->Y
,您可以使它接受
f:&Fn(X)->Y
,类似地,对于FnMut
f:&mut FnMut(X)->Y
。FnOnce更为棘手,因为Rust不支持移动非大小的类型,但您可以尝试将其装箱:

//           ↓ no generic          ↓~~~~~~~~~~~~~~~~~~~~~~~~~~~~ box the closure
fn send_embed(&self, u64, &str, f: Box<FnOnce(String) -> String>) -> Option<u64> 
    where Self: Sync + Send
{
    f("hello".to_string());
    None
}

b.messenger.send_embed(1, "234", Box::new(|a| a));
// note: does not work.
//↓ 没有通用的↓~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 封盖
fn发送嵌入(&self,u64,&str,f:Box String>)->选项
其中Self:Sync+Send
{
f(“hello.to_string());
没有一个
}
b、 messenger.send_embed(1,“234”,Box::new(| a | a));
//注意:不起作用。
但是,从Rust 1.17.0开始,您必须使用:

#![功能(fnbox)]
使用std::boxed::FnBox;
//                                     ↓~~~~
fn发送嵌入(&self,u64,&str,f:Box String>)->选项
其中Self:Sync+Send
{
f(“hello.to_string());
没有一个
}
b、 messenger.send_embed(1,“234”,Box::new(| a | a));
如果您不想使用不稳定功能,可以使用板条箱作为解决方法:

extern crate boxfnonce;
use boxfnonce::BoxFnOnce;

fn send_embed(&self, u64, &str, f: BoxFnOnce<(String,), String>) -> Option<u64> 
    where Self: Sync + Send 
{
    f.call("hello".to_string());
    None
}

b.messenger.send_embed(1, "234", BoxFnOnce::from(|a| a));
extern板条箱fn一次;
使用boxfnonce::boxfnonce;
fn发送\u嵌入(&self,u64,&str,f:BoxFnOnce)->选项
其中Self:Sync+Send
{
f、 调用(“hello.to_string());
没有一个
}
b、 messenger.send_embed(1,“234”,BoxFnOnce::from(|a | a));

特征和特征

在Rust中,您可以使用
trait
定义一个由以下内容组成的接口:

  • 关联类型
  • 关联常数
  • 相关功能
你可以使用特征:

  • 作为泛型参数的编译时界限
  • 作为类型,在引用或指针后面
然而。。。只有一些特征可以直接用作类型。这些特性被标记为对象安全

不幸的是,现在只存在一个
trait
关键字来定义全功能和对象安全特性


插曲:运行时调度是如何工作的?

将特征用作类型时:
&trait
Rc
。。。运行时实现使用fat指针,该指针由以下内容组成:

  • 数据指针
  • 虚拟指针
方法调用通过指向虚拟表的虚拟指针进行调度

对于以下特征:

trait A {
    fn one(&self) -> usize;
    fn two(&self, other: usize) -> usize;
}
对于类型
X
,虚拟表看起来像
(::一,::二)

因此,运行时调度由以下人员执行:

  • 选择表中正确的成员
  • 使用数据指针和参数调用它
这意味着
::两个
看起来像:

fn x_as_a_two(this: *const (), other: usize) -> usize {
    let x = unsafe { this as *const X as &X };
    x.two(other)
}

为什么我不能使用任何特质作为类型?什么是对象安全?

这是技术上的限制

运行时调度有许多无法实现的功能:

  • 关联类型
  • 关联常数
  • 相关的通用功能
  • 签名中与
    Self
    关联的函数
  • 。。。也许其他人
有两种方法可以发出此问题的信号:

  • 早期:拒绝使用
    特征
    作为类型,如果它具有上述任何一项
  • 迟到:拒绝在
    特质
    上使用上述任何一种类型
目前,Rust选择在早期发出信号:不使用上述任何特性的特性是调用对象安全的,可以用作类型

对象不安全的特征不能用作类型,并且会立即触发错误


现在怎么办?

在您的情况下,只需将方法从编译时多态性切换到运行时多态性:

pub trait Messenger : Sync + Send {
    fn send_embed(&self, u64, &str, f: &FnOnce(String) -> String)
        -> Option<u64>;
}

这使得
信使
特征对象安全,因此允许您使用
信使
,…

这与已经讨论过的概念和“对象安全”有关。。。但是我找不到一个好的副本来详细解释这个问题。答案可能是
extern crate boxfnonce;
use boxfnonce::BoxFnOnce;

fn send_embed(&self, u64, &str, f: BoxFnOnce<(String,), String>) -> Option<u64> 
    where Self: Sync + Send 
{
    f.call("hello".to_string());
    None
}

b.messenger.send_embed(1, "234", BoxFnOnce::from(|a| a));
trait A {
    fn one(&self) -> usize;
    fn two(&self, other: usize) -> usize;
}
fn x_as_a_two(this: *const (), other: usize) -> usize {
    let x = unsafe { this as *const X as &X };
    x.two(other)
}
pub trait Messenger : Sync + Send {
    fn send_embed(&self, u64, &str, f: &FnOnce(String) -> String)
        -> Option<u64>;
}
pub trait Messenger : Sync + Send {
    fn send_embed(&self, u64, &str, f: &FnMut(String) -> String)
        -> Option<u64>;
}