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
,类似地,对于FnMutf:&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>;
}