为什么 impl trait 不能用于 return 多个/条件类型?
Why can impl trait not be used to return multiple / conditional types?
我正在尝试获取随机数生成器。由于 OsRng::new()
可能会失败,如果必须的话,我想退回到 thread_rng()
:
extern crate rand; // 0.5.5
use rand::{thread_rng, OsRng, RngCore};
fn rng() -> impl RngCore
{
match OsRng::new() {
Ok(rng) => rng,
Err(e) => thread_rng()
}
}
但是,我收到这条我无法理解的错误消息:
error[E0308]: match arms have incompatible types
--> src/lib.rs:6:5
|
6 | / match OsRng::new() {
7 | | Ok(rng) => rng,
8 | | Err(e) => thread_rng(),
| | ------------ match arm with an incompatible type
9 | | }
| |_____^ expected struct `rand::OsRng`, found struct `rand::ThreadRng`
|
= note: expected type `rand::OsRng`
found type `rand::ThreadRng`
为什么编译器在这里期望 rand::OsRng
而不是 RngCore
的实现?如果我删除 match
并直接 return thread_rng()
,我不会收到上述错误消息。
我不认为这是 的重复,因为另一个问题是询问 如何 一个人可以 return 一个特征函数,这个问题是关于 为什么 编译器不允许我 return 一个特征但要我 return 一个 OsRng
而不是函数的 return 类型。
impl Trait
不等同于返回接口或基础 class 对象。这是一种表达“我不想写我要返回的特定类型的名称”的方式。您仍然返回一个单一的特定类型的值;你只是没有说哪个类型。
这些分支中的每一个都返回不同的类型,因此出现了问题。实现相同的特征是不够的。
在这种特定情况下,您可能需要的是像 Box<dyn RngCore>
.
这样的特征对象
extern crate rand; // 0.6.5
use rand::{rngs::OsRng, thread_rng, RngCore};
fn rng() -> Box<dyn RngCore> {
match OsRng::new() {
Ok(rng) => Box::new(rng),
Err(_) => Box::new(thread_rng()),
}
}
注意:如果您使用的是稍旧版本的 Rust,您可能需要删除 dyn
关键字。它在之前的 (2015) 版本的 Rust 中是可选的。
,但我想提供一个替代解决方法。
如 中所述,您可以创建一个枚举来实现一个特征,如果它的两个组件类型都实现的话。例如:
extern crate rand; // 0.6.5
use rand::{rngs::OsRng, thread_rng, RngCore};
fn rng() -> impl RngCore {
match OsRng::new() {
Ok(rng) => EitherRng::Left(rng),
Err(_) => EitherRng::Right(thread_rng()),
}
}
enum EitherRng<L, R> {
Left(L),
Right(R),
}
impl<L, R> RngCore for EitherRng<L, R>
where
L: RngCore,
R: RngCore,
{
fn next_u32(&mut self) -> u32 {
match self {
EitherRng::Left(l) => l.next_u32(),
EitherRng::Right(r) => r.next_u32(),
}
}
fn next_u64(&mut self) -> u64 {
match self {
EitherRng::Left(l) => l.next_u64(),
EitherRng::Right(r) => r.next_u64(),
}
}
fn fill_bytes(&mut self, b: &mut [u8]) {
match self {
EitherRng::Left(l) => l.fill_bytes(b),
EitherRng::Right(r) => r.fill_bytes(b),
}
}
fn try_fill_bytes(&mut self, b: &mut [u8]) -> Result<(), rand::Error> {
match self {
EitherRng::Left(l) => l.try_fill_bytes(b),
EitherRng::Right(r) => r.try_fill_bytes(b),
}
}
}
either crate 为基本特征提供了很多此类实现。
另请参阅:
我正在尝试获取随机数生成器。由于 OsRng::new()
可能会失败,如果必须的话,我想退回到 thread_rng()
:
extern crate rand; // 0.5.5
use rand::{thread_rng, OsRng, RngCore};
fn rng() -> impl RngCore
{
match OsRng::new() {
Ok(rng) => rng,
Err(e) => thread_rng()
}
}
但是,我收到这条我无法理解的错误消息:
error[E0308]: match arms have incompatible types
--> src/lib.rs:6:5
|
6 | / match OsRng::new() {
7 | | Ok(rng) => rng,
8 | | Err(e) => thread_rng(),
| | ------------ match arm with an incompatible type
9 | | }
| |_____^ expected struct `rand::OsRng`, found struct `rand::ThreadRng`
|
= note: expected type `rand::OsRng`
found type `rand::ThreadRng`
为什么编译器在这里期望 rand::OsRng
而不是 RngCore
的实现?如果我删除 match
并直接 return thread_rng()
,我不会收到上述错误消息。
我不认为这是 OsRng
而不是函数的 return 类型。
impl Trait
不等同于返回接口或基础 class 对象。这是一种表达“我不想写我要返回的特定类型的名称”的方式。您仍然返回一个单一的特定类型的值;你只是没有说哪个类型。
这些分支中的每一个都返回不同的类型,因此出现了问题。实现相同的特征是不够的。
在这种特定情况下,您可能需要的是像 Box<dyn RngCore>
.
extern crate rand; // 0.6.5
use rand::{rngs::OsRng, thread_rng, RngCore};
fn rng() -> Box<dyn RngCore> {
match OsRng::new() {
Ok(rng) => Box::new(rng),
Err(_) => Box::new(thread_rng()),
}
}
注意:如果您使用的是稍旧版本的 Rust,您可能需要删除 dyn
关键字。它在之前的 (2015) 版本的 Rust 中是可选的。
如
extern crate rand; // 0.6.5
use rand::{rngs::OsRng, thread_rng, RngCore};
fn rng() -> impl RngCore {
match OsRng::new() {
Ok(rng) => EitherRng::Left(rng),
Err(_) => EitherRng::Right(thread_rng()),
}
}
enum EitherRng<L, R> {
Left(L),
Right(R),
}
impl<L, R> RngCore for EitherRng<L, R>
where
L: RngCore,
R: RngCore,
{
fn next_u32(&mut self) -> u32 {
match self {
EitherRng::Left(l) => l.next_u32(),
EitherRng::Right(r) => r.next_u32(),
}
}
fn next_u64(&mut self) -> u64 {
match self {
EitherRng::Left(l) => l.next_u64(),
EitherRng::Right(r) => r.next_u64(),
}
}
fn fill_bytes(&mut self, b: &mut [u8]) {
match self {
EitherRng::Left(l) => l.fill_bytes(b),
EitherRng::Right(r) => r.fill_bytes(b),
}
}
fn try_fill_bytes(&mut self, b: &mut [u8]) -> Result<(), rand::Error> {
match self {
EitherRng::Left(l) => l.try_fill_bytes(b),
EitherRng::Right(r) => r.try_fill_bytes(b),
}
}
}
either crate 为基本特征提供了很多此类实现。
另请参阅: