Structs and enums are similar to each other.
When would it be better to use a struct as opposed to an enum (or vice-versa)? Can someone give a clear example where using a struct is preferable to using an enum?
Perhaps the easiest way to explain the fundamental difference is that an enum contains "variants", of which you can only ever have one at a time, whereas a struct contains one or more fields, all of which you must have.
So you might use an enum to model something like an error code, where you can only ever have one at a time:
enum ErrorCode {
NoDataReceived,
CorruptedData,
BadResponse,
}
Enum variants can contain values if needed. For example, we could add a case to ErrorCode like so:
enum ErrorCode {
NoDataReceived,
CorruptedData,
BadResponse,
BadHTTPCode(u16),
}
In this case, an instance of ErrorCode::BadHTTPCode always contains a u16.
This makes each individual variant behave kind of like either a tuple struct or unit struct:
// Unit structs have no fields
struct UnitStruct;
// Tuple structs contain anonymous values.
struct TupleStruct(u16, &'static str);
However, the advantage of writing them as enum variants is that each of the cases of ErrorCode can be stored in a value of type ErrorCode, as below (this would not be possible with unrelated structs).
fn handle_error(error: ErrorCode) {
match error {
ErrorCode::NoDataReceived => println!("No data received."),
ErrorCode::CorruptedData => println!("Data corrupted."),
ErrorCode::BadResponse => println!("Bad response received from server."),
ErrorCode::BadHTTPCode(code) => println!("Bad HTTP code received: {}", code)
};
}
fn main() {
handle_error(ErrorCode::NoDataReceived); // prints "No data received."
handle_error(ErrorCode::BadHTTPCode(404)); // prints "Bad HTTP code received: 404"
}
You can then match on the enum to determine which variant you've been given, and perform different actions depending on which one it is.
By contrast, the third type of struct that I didn't mention above is the most commonly used - it's the type of struct that everyone is referring to when they simply say "struct".
struct Response {
data: Option<Data>,
response: HTTPResponse,
error: String,
}
fn main() {
let response = Response {
data: Option::None,
response: HTTPResponse::BadRequest,
error: "Bad request".to_owned()
}
}
Note that in that case, in order to create a Response, all of its fields must be given values.
Also, the way that the value of response is created (i.e. HTTPResponse::Something) implies that HTTPResponse is an enum. It might look something like this:
enum HTTPResponse {
Ok, // 200
BadRequest, // 400
NotFound, // 404
}
Enums have multiple possibilities. Structs have only one possible "type" of thing they can be. Mathematically, we say a struct is a product type and an enum is a sum of products. If you only have one possibility, use a struct. For example, a point in space is always going to be three numbers. It's never going to be a string, or a function, or something else. So it should be a struct containing three numbers. On the other hand, if you're building a mathematical expression, it could be (for instance) a number or two expressions joined by an operator. It has multiple possibilities, so it should be an enum.
In short, if a struct works, use a struct. Rust can optimize around it, and it's going to be clearer to anyone reading your code what the value is supposed to be treated as.
An Enum is a type with a constrained set of values.
enum Rainbow {
Red,
Orange,
Yellow,
Green,
Blue,
Indigo,
Violet
}
let color = Red;
match color {
Red => { handle Red case },
// all the rest would go here
}
You can store data in the Enum if you need it.
enum ParseData {
Whitespace,
Token(String),
Number(i32),
}
fn parse(input: String) -> Result<String, ParseData>;
A struct is a way to represent a thing.
struct Window {
title: String,
position: Position,
visible: boolean,
}
Now you can make new Window objects that represent a window on your screen.
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 a newbie in Rust.
How can I achieve the same thing as below in Rust?
// C++ code
variant<string, int, float> value;
Update
I am creating a scanner (lexer if you prefer it) in rust. And I want to store a literal that varies (maybe a string, an integer, etc.) to each Token struct.
Without knowing specific requirements, the most natural way to achieve that is with enums. The Rust enum construct can have members that match various types. See this chapter of the Rust book for how to define enums, then how to deconstruct (pattern match) them to get at the data inside:
https://doc.rust-lang.org/book/ch06-00-enums.html
enum Value {
Msg(String),
Count(u32),
Temperature(f32),
}
pub fn value_test() {
let value = Value::Count(7);
// the type is Value, so it could be Value::Count(_), Value::Temperature(_),
// or Value::Msg(_), or you could even add a Value::Empty definition
// that has nothing inside. Or a struct-type with member variables.
if let Value::Msg(inner_str) = value {
println!("This will not run: {}", inner_str);
}
}
This question already has answers here:
Is it possible for one struct to extend an existing struct, keeping all the fields?
(5 answers)
Closed 1 year ago.
That's not regarding Copy trait. I thought about it while designing API, where different requests have same fields. I don't want to copypaste declarations (since that would require copy-pasting & later synchronising e.g. validations), but don't want to use same types for different interfaces to prevent occasional confusion. But it's really generic problem. Here's the illustration.
We have two structs:
struct DataCreateRequest {
name: String
// ... 100 more fields
}
struct DataUpdateRequest {
name: String
// ... 100 more fields
}
What I would like to do is similar to type aliasing, but to make types distinct, something like this:
struct DataCreateRequest {
name: String
// ... 100 more fields
}
clone_type DataUpdateRequest = DataCreateRequest; // could be some macro?
let upd: DataUpdateRequest = DataCreateRequest { ... }; // compiler error, since those are different types
So that we reduce duplication, and still re-specialise them if different request appears later.
Any ideas? Any trait, wrapper, macro magic comes to mind?:)
There are a few ways that might prove useful, but in general a struct cannot "inherit" from another struct in an OOP way.
One way is with wrapper types:
struct LargeStruct {
// lots of fields
}
struct Request(LargeStruct);
struct Response(LargeStruct);
fn foo(request: Request) -> Response {
Response(request.0)
}
This prevents accidentally passing a Request where a Response is expected, and probably the easiest way.
A macro could also work:
macro_rules! foo {
($name:ident) => {
struct $name {
// fields
}
}
}
// then create your structs:
foo!(Request);
foo!(Response);
Personally this seems like a bit of a hack, and you essentially "hide" from the compiler the fact that Request and Response have the same fields, so if you wanted to trivially convert a Request to a Response you'd need something like this:
fn foo(request: Request) -> Response {
Response {
field1: request.field1,
// etc as long as you can tolerate
}
}
I have two different structs with similar functions. Suppose that the program choose which struct to take from the user input.
I want to write something like this
fn main() {
...
let obj = if a == first {
first_object //struct First
} else {
second_object//struct Second
};
obj.read();
obj.write();
obj.some_another_method();
}
I have tried to make an enumeration
pub enum ObjectKind {
FirstObject(First),
SecondObject(Second)
}
But I cannot use methods in this case
let something = ObjectKind::FirstObject(first_object);
something.read()
//no method named `read` found for enum `structs::ObjectKind` in the current scope
//method not found in `structs::ObjectKind`
But I cannot use methods in this case
An enum is a proper type in and of itself, it's not a "union" of existing types. You can just define the methods on the enum to forward to the relevant object e.g.
impl ObjectKind {
fn read(&self) {
match self {
FirstObject(f) => f.read()
SecondObject(s) => s.read()
}
}
}
as it would probably be a bit repetitive, you can use a macro to make the forwarding easier.
Alternatively, you might be able to define a trait for the common behaviour and use a trait object to dynamically dispatch between the two, but that can be somewhat restrictive.
I'm new to Rust and there is a function in actix-web that intrigue me. To create a router in actix, we need to pass a function (handle_get_insert for example) that implements actix_web::Responder
// main.rs
cfg.service(
web::resource("/items/get")
.route(web::get().to(handle_get_item))
.route(web::head().to(|| HttpResponse::MethodNotAllowed())),
// handle_get_item is a function that returns any type that implement use actix_web::Responder
// Here Responder means any type that it return must have #[derive(Serialize, Deserialize)]
pub async fn handle_get_item(....) -> impl Responder {
match reply {
Ok(item_doc) => {
let result = GetReply {
id: String::from(inf.id.to_string()),
doc: Some(item_doc),
ok: true,
};
// convert the result into json
return web::Json(result);
}
Err(e) => {
let result = ErrorReply {
error: format!("{}", e),
ok: false,
};
// error is occurred here: expected struct "GetReply", found struct "ErrorReply"
return web::Json(result);
}
}
}
this handle_get_item can return two type of value (GetReply for data and ErrorReply if an error occurred). Both types have the same traits but with different fields in it.
To my surprise, Rust only recognize one of them as the return type, and throws an error on the ErrorReply:
error: mismatched types
label: expected struct "GetReply", found struct "ErrorReply"
which means that Rust only recognizes GetReply as the returned value and forces each exit point to implements the same GetReply type.
Does it possible to return two types ? or this is not supposed to be happen in actix ?
Both types have the same traits but with different fields in it.
Yes but as Ivan C noted that's not what impl Trait does. impl Trait is essentially a placeholder for a normal type which implements the trait (it also makes the return type opaque which lets you e.g. return private types from pub functions).
Does it possible to return two types ?
Technically no, a Rust function can only return values of one type, however there are usually ways around this. One is to return a dynamically dispatched object via Box. This is slightly less efficient, but if the trait is object-safe (note: I've no idea if Responder is) then it lets you leverage trait objects to kinda-sorta return different types from the same function.
An alternative is that the one trait is basically just an intermediate step and ends up converting to a single concrete type at the end. While you'd usually return something implementing the trait for convenience you can… take the final step yourself "by hand".
In Actix, I expect that's HttpResponse, which you can either build "by hand" or using Responder::respond_to.