Rust: Enum vs Impl Trait vs? [duplicate] - enums

I'm trying to get a random number generator. Since OsRng::new() can fail, I'd like to fall back to thread_rng() if I have to:
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()
}
}
However, I get this error message which I cannot understand:
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`
Why is the compiler expecting rand::OsRng here instead of an implementation of RngCore? If I remove the match and directly return thread_rng(), I don't get above error message.
I do not believe that this is a duplicate of How do I return an instance of a trait from a method?, as the other question is asking about how one can return a trait from a function, and this question is about why the compiler will not allow me to return a trait but wants me to return an OsRng which is not the return type of the function.

impl Trait is not equivalent to returning an interface or base class object. It's a way of saying "I don't want to write the name of the specific type I'm returning". You're still returning a value of a single, specific type; you just aren't saying which type.
Each of those branches is returning different types, hence the problem. Implementing the same trait is not enough.
What you likely want in this specific case is a trait object like 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()),
}
}
Note: if you are using a slightly older version of Rust, you may need to remove the dyn keyword. It's optional in the previous (2015) edition of Rust.

DK. has already explained why, but I'd like to provide an alternative workaround.
As mentioned in Conditionally iterate over one of several possible iterators, you can create an enum that implements a trait if both of its component types do. For example:
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),
}
}
}
The either crate provides a lot of these types of implementations for fundamental traits.
See also:
How do I conditionally return different types of futures?

Related

Should I implement a trait also by value to allow for better performance?

Let's say I have the following enums :
enum SomeEnum {
V1(i32),
V2(f32),
V3(String),
V4(Vec<i32>),
}
#[derive(Debug)]
enum SomeEnumConversionError {
OutOfTypeRange,
StringParse,
TooManyElements,
}
And I want the following trait to work for both values and references of the first enum; So I implement it for the reference to save some time and a few lines of duplicate code (Considering I have to do almost the same for all other variants and the real enum I'm working with has about 30 variants) :
impl TryFrom<&SomeEnum> for i32 {
type Error = SomeEnumConversionError;
fn try_from(value: &SomeEnum) -> Result<Self, Self::Error> {
match value {
SomeEnum::V1(v) => Ok(*v),
SomeEnum::V2(v) => {
let i = *v as i32;
if (i as f32) == *v {
Ok(i)
} else {
Err(SomeEnumConversionError::OutOfTypeRange)
}
}
SomeEnum::V3(v) => v
.parse::<i32>()
.map_err(|_| SomeEnumConversionError::StringParse),
SomeEnum::V4(v) if v.len() == 1 => Ok(v[0]),
SomeEnum::V4(v) => Err(SomeEnumConversionError::TooManyElements),
}
}
}
impl TryFrom<SomeEnum> for i32 {
type Error = SomeEnumConversionError;
fn try_from(value: SomeEnum) -> Result<Self, Self::Error> {
(&value).try_into()
}
}
Now, if I have the following code as an example usage :
fn some_func() {
let a = SomeEnum::V4(vec![4i32]);
let b: Result<i32, _> = a.try_into();
println!("{}", b.unwrap());
}
The question is, how is this (performance-wise, considering the conversion might be called in a loop) compared to when I implement the trait for both value and reference type (rather than just calling it by reference from value implementation) ?
Does the compiler do some optimization magic here that will eventually make it behave as if the variable a has been actually moved to the function implementation calls ?
Or do I need to also implement the whole conversion function for the value type to ensure this ?
If there are better alternatives to the whole plan, I'm all ears.

How to implement a trait like `Debug` specifically for certain enum variants? [duplicate]

Let's say I have the following Enum
enum MyEnum {
VariantA,
VariantB,
VariantC,
}
I can derive the PartialEq trait for the whole enum by doing so
#[derive(PartialEq)]
enum MyEnum {
VariantA,
VariantB,
VariantC,
}
What I want to do, is derive the trait but only to particular variants and not the whole enum. Is that possible? (or does it even make sense?).
Assuming you have a setup like:
#[derive(PartialEq)]
struct VarB{
pub value: u32,
}
#[derive(PartialEq)]
enum MyEnum{
VarA(VarA),
VarB(VarB)
}
VarA comes from a different crate and you can't compile due to it not having derived PartialEq (or any other external trait).
You can solve that with the newtype pattern (assuming you have access to the relevent fields / accessors)
struct MyVarA(VarA);
impl PartialEq for MyVarA{
fn eq(&self, other: &MyVarA) -> bool {
self.0.value == other.0.value
}
fn ne(&self, other: &MyVarA) -> bool {
self.0.value != other.0.value
}
}
#[derive(PartialEq)]
struct VarB{
value: u32,
}
#[derive(PartialEq)]
enum MyEnum{
VarA(MyVarA),
VarB(VarB)
}
further informations:
https://doc.rust-lang.org/rust-by-example/generics/new_types.html
What I want to do, is derive the trait but only to particular variants and not the whole enum. Is that possible? (or does it even make sense?).
This doesn't really make sense.
Traits are implemented for types. An enum is a type and its variants are its values. Your question is equivalent to asking if you could implement a trait for some Strings but not others.
If it is acceptable for unsupported variants to always return false, similar to how f32's PartialEq implementation returns false whenever you compare a NaN value, then you can write that impl by hand:
impl PartialEq for MyEnum {
fn eq(&self, other: &MyEnum) -> bool {
use MyEnum::*;
match (self, other) {
// VariantA and VariantB are supported
(VariantA(value), VariantA(other_value)) => value == other_value,
(VariantB(value), VariantB(other_value)) => value == other_value,
// Any other combinations of variants end up here
_ => false,
}
}
}
Note that you must not implement Eq this way, since implementations of Eq may be assumed to be total, which this is not.

How do I have a trait field in a struct?

I have some code that I want to turn into a crate. But it includes a structure that contains a field that I want to be provided by the user of the crate. But I need functionality from that field, so I want to specify it as a trait.
pub trait RoleTrait {
fn owner<T: RoleTrait>() -> T;
fn order<T: RoleTrait>(&self) -> usize;
}
pub struct RequestInfo<Role: RoleTrait + PartialEq> {
role: Option<Role>,
name: String,
}
impl<Role: RoleTrait> RequestInfo<Role>
where
Role: std::cmp::PartialEq,
{
fn name(&self) -> String {
self.name.to_string()
}
fn role(&self) -> &Option<Role> {
&self.role
}
fn is_owner(&self) -> bool {
if let Some(role) = self.role {
role == Role::owner()
} else {
false
}
}
fn order(&self) -> usize {
if let Some(role) = self.role {
role.order() + 1
} else {
0
}
}
fn from(name: String) -> RequestInfo<Role> {
RequestInfo::<Role> {
role: None,
name: name,
}
}
fn with_role(name: String, role: Role) -> RequestInfo<Role> {
RequestInfo::<Role> {
role: Some(role),
name: name,
}
}
}
With two implementations of RoleTrait:
#[derive(PartialEq)]
pub enum CourseRole {
Professor,
Marker,
Student,
}
impl RoleTrait for CourseRole {
fn owner<T: RoleTrait>() -> T {
CourseRole::Professor
}
fn order<T: RoleTrait>(&self) -> usize {
if *self == CourseRole::Professor {
0
} else {
1
}
}
}
#[derive(PartialEq)]
pub enum BlogRole {
Owner,
Blogger,
}
impl RoleTrait for BlogRole {
fn owner<T: RoleTrait>() -> T {
BlogRole::Owner
}
fn order<T: RoleTrait>(&self) -> usize {
if *self == BlogRole::Owner {
0
} else {
1
}
}
}
I get 3 errors with this.
error[E0282]: type annotations needed
--> src/main.rs:28:18
|
28 | role.order() + 1
| ^^^^^ cannot infer type for `T`
error[E0308]: mismatched types
--> src/main.rs:55:9
|
54 | fn owner<T: RoleTrait>() -> T {
| - expected `T` because of return type
55 | CourseRole::Professor
| ^^^^^^^^^^^^^^^^^^^^^ expected type parameter, found enum `CourseRole`
|
= note: expected type `T`
found type `CourseRole`
error[E0308]: mismatched types
--> src/main.rs:72:9
|
71 | fn owner<T: RoleTrait>() -> T {
| - expected `T` because of return type
72 | BlogRole::Owner
| ^^^^^^^^^^^^^^^ expected type parameter, found enum `BlogRole`
|
= note: expected type `T`
found type `BlogRole`
(and the second error repeated for the other enum)
Frankly, I'm surprised (and pleased!) that some of my code is valid (like the references to owner in the trait). I had a lot more errors when I started writing this question, but I can't figure out these remaining ones since T looks so clear and rustc seems to have already figured out harder things. In the last 2 errors, it's almost like it doesn't realize that there is an implementation of the trait for the enum because it's in the middle of defining that implementation (but it obviously understands that in other places).
Something feels a little "off" with this trait:
pub trait RoleTrait {
fn owner<T: RoleTrait>() -> T;
fn order<T: RoleTrait>(&self) -> usize;
}
The owner method doesn't have a receiver (e.g. self), so it seems unnecessary to introduce a new type parameter; Self will do the same thing.
In order, having a separate T is not exactly the same thing as just using Self - it allows T and Self to be a completely different implementations of RoleTrait. But this feels like quite a strange and unusual requirement, especially since T doesn't appear in the method signature.
Your code can be fixed quite simply by following the more typical pattern:
pub trait RoleTrait {
fn owner() -> Self;
fn order(&self) -> usize;
}
This small change leads to all the type errors being resolved, and just leaving a couple of small borrow errors (playground), which can be quite easily addressed (playground).

Derive a Trait for particular variants

Let's say I have the following Enum
enum MyEnum {
VariantA,
VariantB,
VariantC,
}
I can derive the PartialEq trait for the whole enum by doing so
#[derive(PartialEq)]
enum MyEnum {
VariantA,
VariantB,
VariantC,
}
What I want to do, is derive the trait but only to particular variants and not the whole enum. Is that possible? (or does it even make sense?).
Assuming you have a setup like:
#[derive(PartialEq)]
struct VarB{
pub value: u32,
}
#[derive(PartialEq)]
enum MyEnum{
VarA(VarA),
VarB(VarB)
}
VarA comes from a different crate and you can't compile due to it not having derived PartialEq (or any other external trait).
You can solve that with the newtype pattern (assuming you have access to the relevent fields / accessors)
struct MyVarA(VarA);
impl PartialEq for MyVarA{
fn eq(&self, other: &MyVarA) -> bool {
self.0.value == other.0.value
}
fn ne(&self, other: &MyVarA) -> bool {
self.0.value != other.0.value
}
}
#[derive(PartialEq)]
struct VarB{
value: u32,
}
#[derive(PartialEq)]
enum MyEnum{
VarA(MyVarA),
VarB(VarB)
}
further informations:
https://doc.rust-lang.org/rust-by-example/generics/new_types.html
What I want to do, is derive the trait but only to particular variants and not the whole enum. Is that possible? (or does it even make sense?).
This doesn't really make sense.
Traits are implemented for types. An enum is a type and its variants are its values. Your question is equivalent to asking if you could implement a trait for some Strings but not others.
If it is acceptable for unsupported variants to always return false, similar to how f32's PartialEq implementation returns false whenever you compare a NaN value, then you can write that impl by hand:
impl PartialEq for MyEnum {
fn eq(&self, other: &MyEnum) -> bool {
use MyEnum::*;
match (self, other) {
// VariantA and VariantB are supported
(VariantA(value), VariantA(other_value)) => value == other_value,
(VariantB(value), VariantB(other_value)) => value == other_value,
// Any other combinations of variants end up here
_ => false,
}
}
}
Note that you must not implement Eq this way, since implementations of Eq may be assumed to be total, which this is not.

How can I implement a trait in scope for an enum of existing types for which the trait is implemented?

How can I implement a trait in scope for an enum of existing types for which the trait is implemented?
I have this:
extern crate pnet;
use pnet::packet::ipv4::Ipv4Packet;
use pnet::packet::ipv6::Ipv6Packet;
enum EthernetType {
IPv4,
ARP,
VLAN,
IPv6,
Unknown(u16),
}
enum IPPacket<'a> {
IPv4(Ipv4Packet<'a>),
IPv6(Ipv6Packet<'a>),
}
fn ip_decode(pkt: &[u8]) -> IPPacket {
let version = (pkt[0] & 0xf0) >> 4;
if version == 4 {
IPPacket::IPv4(Ipv4Packet::new(&pkt).unwrap())
} else {
IPPacket::IPv6(Ipv6Packet::new(&pkt).unwrap())
}
}
fn main() {
// Parse ethernet packet here...
// ...
let ip_packet = ip_decode(b"deadbeef");
println!("{:?}", ip_packet.payload());
}
The compiler complains that I have not implemented the Packet trait for my enum:
error[E0599]: no method named `payload` found for type `IPPacket<'_>` in the current scope
--> src/main.rs:32:32
|
14 | enum IPPacket<'a> {
| ----------------- method `payload` not found for this
...
32 | println!("{:?}", ip_packet.payload());
| ^^^^^^^
|
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `payload`, perhaps you need to implement it:
candidate #1: `pnet::packet::Packet`
I thought that the Packet trait would be derived through Ipv4Packet<'a> and Ipv6Packet<'a>?
How can I implement a trait in scope for an enum of existing types
You implement a trait for an enum the same way you implement a trait for a struct:
trait Noise {
fn noise(&self);
}
enum Foo {
Bar,
Baz,
}
impl Noise for Foo {
fn noise(&self) {
match self {
Foo::Bar => println!("bar bar bar"),
Foo::Baz => println!("baz baz baz"),
}
}
}
an enum of existing types for which the trait is implemented
I thought that the Packet trait would be derived
It is not. Doing so would prevent people from implementing their own code for the trait if they needed. It also wouldn't work in all cases, such as when one variant didn't implement it.
trait Noise {
fn noise(&self);
}
struct Bar;
impl Noise for Bar {
fn noise(&self) {
println!("bar bar bar");
}
}
struct Baz;
impl Noise for Baz {
fn noise(&self) {
println!("baz baz baz");
}
}
enum Foo {
Bar(Bar),
Baz(Baz),
}
impl Noise for Foo {
fn noise(&self) {
match self {
Foo::Bar(bar) => bar.noise(),
Foo::Baz(baz) => baz.noise(),
}
}
}
It's conceptually possible that the language could be extended to support some annotation to do this, but I've never heard of anyone suggesting it. You could consider creating an RFC to add it.
Perhaps you can go back to the source that taught you this and correct the problem at the root to prevent other peopl from being confused in the same manner.
See also:
Can traits be used on enum types?

Resources