I am learning rust and have come across enums. I understand why they might be useful for organising variables. It is also far more difficult to access data in said enums. My question comes down to this: why do people use enums and is there an easy way to retrieve the data from them?
My current solution to the latter is to use a match statement:
enum Breakfast {
Toast(String),
}
fn main() {
let breakfast = Breakfast::Toast(String::from("Buttered"));
match breakfast {
Breakfast::Toast(t) => println!("{}",t),
_ => panic!("I'm Hungry!"),
}
}
This is a very inefficient, are there any other ways to do this?
I understand why they might be useful for organising variables.
I've got no idea what that means.
My question comes down to this: why do people use enums
Because they allow making invalid states unrepresentable1: enums allow exclusive representations.
and is there an easy way to retrieve the data from them?
match, if let, utility functions which bundle the access or even provides higher-level operations on them.
Your example is too detached from any information to make any judgement upon it (e.g. on whether you're misusing enums) aside from "the compiler literally tells you that the _ case is useless".
But for instance the Option and Result types are enums. Because an Option is either Some or None, and a Result is either Ok or Err. Enums allow this information to be part of the typesystem, and thus the compiler to assist with checking it.
1: though a common issue is what states are actually invalid, there are domains where it's not clear cut, the validity of a given datum can even vary based on domain!
I think the previous answer misinterpreted your question.
A simple let statement can also destructure the enum. However usually there is more than one enum variant.
enum Breakfast {
Toast(String),
}
fn main() {
let breakfast = Breakfast::Toast(String::from("Buttered"));
let Breakfast::Toast(t) = breakfast;
println!("{}", t);
}
enum Breakfast {
Toast(String),
Eggs(String)
}
fn main() {
let breakfast = Breakfast::Toast(String::from("Buttered"));
let _breakfast2 = Breakfast::Eggs(String::from("Over Easy"));
if let Breakfast::Toast(t) = breakfast {
println!("{}", t);
} ;
}
Related
I am writing a Rust wrapper for a C API. It contains a function that may fail, in which case it returns an error code encoded as an int. Let's call these SOME_ERROR and OTHER_ERROR, and they will have the values 1 and 2, respectively. I want to write an enum wrapping these error codes, as follows:
// Declared in a seperate C header
const SOME_ERROR: c_int = 1;
const OTHER_ERROR: c_int = 2;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)]
enum ErrorCodeWrapper {
SomeError = SOME_ERROR,
OtherError = OTHER_ERROR,
}
Here comes my first question. It does not seem to be possible to specify std::os::raw::c_int as the underlying type of an enum. But I do feel like it should be, as int isn't required to be 32 bits wide. Is there any way to achieve this?
I'd then like some methods to convert to and from a raw error code:
use std::os::raw::c_int;
impl ErrorCodeWrapper {
fn from_raw(raw: c_int) -> Option<Self> {
match raw {
SOME_ERROR => Some(Self::SomeError),
OTHER_ERROR => Some(Self::OtherError),
_ => None
}
}
unsafe fn from_raw_unchecked(raw: c_int) -> Self {
*(&raw as *const _ as *const Self)
}
fn as_raw(self) -> c_int {
unsafe { *(&self as *const _ as *const c_int) }
}
}
The only way I could find to "bit-cast" c_int to and from ErrorCodeWrapper is to do it C-style, by casting a pointer and then dereferencing it. This should work as ErrorCodeWrapper and int have the same size and alignment, and the value of every ErrorCodeWrapper variant maps to its corresponding error code. However, this solution is a bit to hackery for my taste; is there a more idiomatic one, like C++'s std::bit_cast?
Furthermore, is it possible to replace the match statement in ErrorCodeWrapper::from_raw with a simple validity check, for simpler code in the case of more variants?
The last bit of code, the necessary error implementations:
use std::{fmt::Display, error::Error};
impl Display for ErrorCodeWrapper {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", match self {
Self::SomeError => "some error",
Self::OtherError => "some other error",
})
}
}
impl Error for ErrorCodeWrapper {}
Now let's imagine a second wrapper, SuperErrorCodeWrapper, that includes some or all of the variants of ErrorCodeWrapper, with the same description and everything. That would mean that either:
One could "factor out" the common variants of ErrorCodeWrapper and SuperErrorCodeWrapper into a separate enum. ErrorCodeWrapper and SuperErrorCodeWrapper would then have a variant containing this enum. However I am not really fond of this kind of nesting, which would seem arbitrary when focusing on one particular error.
Duplicating the common variants across both enums.
The latter would add a lot to the existing boilerplate. Could a macro be a viable option to handle this?
Is there a library that could handle all this for me?
Here comes my first question. It does not seem to be possible to specify std::os::raw::c_int as the underlying type of an enum. But I do feel like it should be, as int isn't required to be 32 bits wide. Is there any way to achieve this?
No. There was an RFC in 2016 (I can't even access the RFC text. it seems it was removed), but it was closed:
We discussed in the #rust-lang/lang meeting and decided that while the RFC is well-motivated, it doesn't sufficiently address the various implementation complexities that must be overcome nor the interaction with hygiene. It would make sense to extend the attribute system to support more general paths before considering this RFC (but that is a non-trivial undertaking).
The best you can do is to use #[cfg_attr] with all configurations. c_int is defined here as, and all current options are
#[cfg_attr(any(target_arch = "avr", target_arch = "msp430"), repr(i16))]
#[cfg_attr(not(any(target_arch = "avr", target_arch = "msp430")), repr(i32))]
enum ErrorCodeWrapper { ... }
is there a more idiomatic one, like C++'s std::bit_cast?
Yes; std::mem::transmute().
One could "factor out" the common variants of ErrorCodeWrapper and SuperErrorCodeWrapper into a separate enum. ErrorCodeWrapper and SuperErrorCodeWrapper would then have a variant containing this enum.
If you do that, you lose the ability to transmute() (or pointer cast, it doesn't matter), as they'll be no longer layout compatible with int.
Could a macro be a viable option to handle this?
Probably yes.
Is there a library that could handle all this for me?
I don't know all library that handles all of this (although it is possible that one exists), but there is thiserror (and friends) for the Error and Display implementations, and strum::FromRepr that can help you with from_raw().
I'm in the process of learning Rust, and there's some functionality around ownership and references that consistently stumps me:
struct ThingOne {
value: u32
}
// Assume I can't change this signature to simply own thing_ref. Maybe
// it's from third-party code.
struct ThingTwo<'a> {
thing_ref: &'a ThingOne
}
fn test<'a>() -> ThingTwo<'a> {
let thing1 = ThingOne { value: 1 };
ThingTwo { thing_ref: &thing1 }
}
Obviously, this can't work -- thing1 is stack allocated, the reference can't safely escape the function. However, this pattern seems like a common one, and I can't find a good workaround. I could change the return type to a tuple (ThingOne, ThingTwo) so that the caller has moved ownership of thing1, but then I'd still run into issues trying to create that reference to thing1 when initializing the ThingTwo result. I could use an Rc<ThingOne> but that feels like a very heavy handed solution.
Is there an idiomatic way to handle this kind of code? Or is there some other approach that's better than the way that test() function is structured?
I created a procedural macro that implements a trait, but in order for this to work I need to get the raw bytes for every field. The problem is how to get the bytes of a field differs depending on the type of field.
Is there some way of testing if a function exists on a field and if it does not tries another function?
E.g. something like this:
if item.field::function_exist {
//do code
} else {
//do other code
}
Currently I am looking at creating another trait/member function that I just have to create for all primitives and create a procedural macro for larger fields such as structs. For example:
if item.field::as_bytes().exists {
(&self.#index).as_bytes()
} else {
let bytes = (&self.#index).to_bytes();
&bytes
}
With a string, it has a as_bytes member function, while i32 does not. This means I need extra code, when the member field of the struct is not a string. I might need a match rather than an if, but the if will suffice for the example.
Is it possible to tell if a field is a certain type or implements a certain method in a procedural macro?
No, it is not.
Macros operate on the abstract syntax tree (AST) of the Rust code. This means that you basically just get the characters that the user typed in.
If user code has something like type Foo = Option<Result<i32, MyError>>, and you process some code that uses Foo, the macro will not know that it's "really" an Option.
Even if it did know the type, knowing what methods are available would be even harder. Future crates can create traits which add methods to existing types. At the point in time that the procedural macro is running, these crates may not have even been compiled yet.
I am looking at creating another trait/member function that I just have to create for all primitives and create a procedural macro for larger fields such as structs.
This is the correct solution. If you look at any existing well-used procedural macro, that's exactly what it does. This allows the compiler to do what the compiler is intended to do.
This is also way better for maintainability — now these primitive implementations live in a standard Rust file, as opposed to embedded inside of a macro. Much easier to read and debug.
Your crate will have something like this:
// No real design put into this trait
trait ToBytes {
fn encode(&self, buf: &mut Vec<u8>);
}
impl ToBytes for str {
fn encode(&self, buf: &mut Vec<u8>) {
buf.extend(self.as_bytes())
}
}
// Other base implementations
And your procedural macro will implement this in the straightforward way:
#[derive(ToBytes)]
struct Foo {
a: A,
b: B,
}
becomes
impl ToBytes for Foo {
fn encode(&self, buf: &mut Vec<u8>) {
ToBytes::encode(&self.a, buf);
ToBytes::encode(&self.b, buf);
}
}
As a concrete example, Serde does the same thing, with multiple ways of serializing to and from binary data:
Bincode
CBOR
MessagePack
etc.
In my short Rust experience I ran into this pattern several times, and I'm not sure if the way I solve it is actually adequate...
Let's assume I have some trait that looks like this:
trait Container {
type Item;
fn describe_container() -> String;
}
And some struct that implements this trait:
struct ImAContainerType;
struct ImAnItemType;
impl Container for ImAContainerType {
type Item = ImAnItemType;
fn describe_container() -> String { "some container that contains items".to_string() }
}
This may be a container that has a knowledge about type of items it contains, like in this example, or, as another example, request which knows what type of response should be returned, etc.
And now I find myself in a situation, when I need to implement a function that takes an item (associated type) and invokes a static function of the container (parent trait). This is the first naive attempt:
fn describe_item_container<C: Container>(item: C::Item) -> String {
C::describe_container()
}
This does not compile, because associated types are not injective, and Item can have several possible Containers, so this whole situation is ambiguous. I need to somehow provide the actual Container type, but without providing any container data. I may not have the container data itself at all when I invoke this function!
In search for a solution, I find the documentation for std::marker::PhantomData. It says:
PhantomData allows you to describe that a type acts as if it stores a value of type T, even though it does not.
This has to be the Rust's replacement for Haskell's Proxy type, right? Let's try to use it:
fn describe_item_container<C: Container>(container: PhantomData<C>, item: C::Item) -> String {
C::describe_container()
}
let s = describe_item_container(PhantomData::<PhantomData<ImAContainerType>>, ImAnItemType);
println!("{}", s);
Compiling... Error:
error[E0277]: the trait bound `std::marker::PhantomData<ImAContainerType>: Container` is not satisfied
I ask #rust-beginners and get a response: PhantomData is not meant to be used that way at all! Also I got an advice to simply make a backward associated type link from Item to Container. Something like this:
trait Item {
type C: Container;
}
fn describe_item_container<I: Item>(item: I) -> String {
I::C::describe_container()
}
It should work, but makes things much more complicated (especially for cases when item can be placed in different container kinds)...
After a lot more experimentation, I do the following change and everything compiles and works correctly:
let s = describe_item_container(PhantomData::<ImAContainerType>, ImAnItemType);
println!("{}", s);
The change is ::<PhantomData<ImAContainerType>> to ::<ImAContainerType>.
Playground example.
It works, but now I'm completely confused. Is this the correct way to use PhantomData? Why does it work at all? Is there some other, better way to provide type-only argument to a function in Rust?
EDIT: There is some oversimplification in my example, because in that particular case it would be easier to just invoke ImAContainerType::describe_container(). Here is some more complicated case, when the function actually does something with an Item, and still requires container type information.
If you want to pass a type argument to a function, you can just do it. You don't have to leave it out to be inferred.
This is how it looks for your second example (playground):
fn pack_item<C: Container>(item: C::Item) -> ItemPacket {
ItemPacket {
container_description: C::describe_container(),
_payload: item.get_payload(),
}
}
fn main() {
let s = pack_item::<ImAContainerType>(ImAnItemType);
println!("{}", s.container_description);
let s = pack_item::<ImAnotherContainerType>(ImAnItemType);
println!("{}", s.container_description);
}
Is there a way to print out a complete list of available members of a type or instance in Rust?
For example:
In Python, I can use print(dir(object))
In C, Clang has a Python API that can parse C code and introspect it.
Being unfamiliar with Rust tools, I'm interested to know if there is some way to do this, either at run-time or compile-time, either with compiler features (macros for example), or using external tools.
This question is intentionally broad because the exact method isn't important. It is common in any language to want to find all of a variable's methods/functions. Not knowing Rust well, I'm not limiting the question to specific methods for discovery.
The reason I don't define the exact method is that I assume IDEs will need this information, so there will need to be some kinds of introspection available to support this (eventually). For all I know, Rust has something similar.
I don't think this is a duplicate of Get fields of a struct type in a macro since this answer could include use of external tools (not necessarily macros).
Is there a way to print out a complete list of available members of a type or instance in Rust?
Currently, there is no such built-in API that you can get the fields at runtime. However you can retrieve fields by using two different ways.
Declarative Macros
Procedural Macros
Solution By Using Declarative Macro
macro_rules! generate_struct {
($name:ident {$($field_name:ident : $field_type:ty),+}) => {
struct $name { $($field_name: $field_type),+ }
impl $name {
fn introspect() {
$(
let field_name = stringify!($field_name);
let field_type = stringify!($field_type);
println!("Field Name: {:?} , Field Type: {:?}",field_name,field_type);
)*
}
}
};
}
generate_struct! { MyStruct { num: i32, s: String } }
fn main() {
MyStruct::introspect();
}
This will give you the output:
Field Name: "num" , Field Type: "i32"
Field Name: "s" , Field Type: "String"
Playground
Solution Using Procedural Macro
Since procedural macros are more complicated from the declarative macros, you better to read some references(ref1, ref2, ref3) before starting.
We are going to write a custom derive which is named "Instrospect". To create this custom derive, we need to parse our struct as a TokenStream with the help of syn crate.
#[proc_macro_derive(Introspect)]
pub fn derive_introspect(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as ItemStruct);
// ...
}
Since our input can be parsed as ItemStruct and ItemStruct has the fields() method in it, we can use this to get fields of our struct.
After we get these fields, we can parse them as named and we can print their field name and field type accordingly.
input
.fields
.iter()
.for_each(|field| match field.parse_named() {
Ok(field) => println!("{:?}", field),
Err(_) => println!("Field can not be parsed successfully"),
});
If you want to attach this behavior to your custom derive you can use the following with the help of the quote crate:
let name = &input.ident;
let output = quote! {
impl #name {
pub fn introspect(){
input
.fields
.iter()
.for_each(|field| match field.parse_named() {
Ok(field) => println!("{:?}", field),
Err(_) => println!("Field can not be parsed successfully"),
});
}
}
};
// Return output TokenStream so your custom derive behavior will be attached.
TokenStream::from(output)
Since the behaviour injected to your struct as introspect function, you can call it in your application like following:
#[derive(Introspect)]
struct MyStruct {
num: i32,
text: String
}
MyStruct::introspect();
Note: Since the example you are looking for similar to this question. This Proc Macro Answer and Declarative Macro Answer should give you insight as well
To expand on my comment, you can use rustdoc, the Rust documentation generator, to view almost everything you're asking for (at compile time). rustdoc will show:
Structs (including public members and their impl blocks)
Enums
Traits
Functions
Any documentation comments written by the crate author with /// or //!.
rustdoc also automatically links to the source of each file in the [src] link.
Here is an example of the output of rustdoc.
Standard Library
The standard library API reference is available here and is available for anything in the std namespace.
Crates
You can get documentation for any crate available on crates.io on docs.rs. This automatically generates documentation for each crate every time it is released on crates.io.
Your Project
You can generate documentation for your project with Cargo, like so:
cargo doc
This will also automatically generate documentation for your dependencies (but not the standard library).
I have written a very simple crate which uses procedural macro. It gives you access to members information plus some simple information about struct/enum you use. Information about methods can not be given because procedural macros simply can't get this information, and as far as I know, there are no any methods which may give such information.
I don't think there is anything that will do this out of the box.
It may be possible to write a compiler plugin which can do that by examining the AST.
If you need the field names inside your program then you probably need to use macros. Either wrap your struct definition in macro and pattern match to create some function to get their names, or use procedural macro to derive structs for traits with such functions.
See examples in syn for derived traits. In particular, see syn::Data::Struct which has fields.
According to question of #AlexandreMahdhaoui, I would say: at least on latest Rust versions the proc_macro from accepted answer will not work, because you will need to pass tokens into quote! using "#". So you could try smth like next:
use proc_macro::{TokenStream};
use quote::{quote, ToTokens};
use syn::{parse_macro_input, ItemStruct};
#[proc_macro_derive(Introspect)]
pub fn derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as ItemStruct);
let ident = input.ident;
let field_data = input.fields.iter().map(|f| {
let field_type = f.ty.clone();
format!(
"Name={}, Type={}",
f.ident.clone().unwrap().to_string(),
quote!(#field_type).to_string()
)
}).collect::<Vec<_>>();
let output = quote! {
impl #ident {
pub fn introspect() {
println!("{:#?}", vec![#(#field_data),*]);
}
}
};
TokenStream::from(output)
}
#[derive(Introspect)]
struct Test {
size: u8,
test_field: u8,
}
fn main() {
Test::introspect();
}
Regarding methods, defined in impl I didn't find any info in output, so not sure if it possible. Probably somebody could share in comments ?
I use something like this:
println!("{:?}", variable); // struct, enum whatever
If it's a large type, use the # version:
println!("{:#?}", variable); // struct, enum whatever