I could have a structure like the following:
struct Example {
state: State,
properties: HashMap<String, String>,
}
enum State {
a, b, c,
}
If the Example structure is the only user of the State enum, I think it would make sense to declare it in the struct:
struct Example {
state: enum { a, b, c },
properties: HashMap<String, String>,
}
Is there any valid syntax for this?
No. There is no such syntax.
You can check the Rust grammar for structs, the type of a field must be a type expression. Type expressions cannot create new types.
Related
I'm new to Rust and just wondering if there's an equivalent of the keyof (like in TypeScript) operator in Rust.
I don't know if this is possible, but I'm trying to access the key and value of a struct within another struct.
example:
interface Events {
msg:(data:string)=>any,
abort:()=>any
}
class EventEmitter<T>{
on(event: keyof T,callback:T[keyof T])
}
I'm trying to achieve the same on function in rust.
struct Events {
msg: Fn(&str)->(),
abort: Fn()->(),
}
struct EventEmitter<T> {
pub listeners: Vec<Listener<T>>,
}
Context: I'm trying to recreate EventEimtter exactly like node.js & ts
What you're describing is reflection. As mentioned in the comments to your question, Rust does not have reflection as a native feature. Using a String to access members of your struct could potentially create unpredictable or undefined behavior.
If it truly is important to access members of your struct that have the same type, you could look into creating a trait called "Keyable" or something similar. This struct should (probably) look something like this.
pub trait Keyable<T> {
fn get_string(&self, for_key: T) -> Option<&String>;
fn get_i32(&self, key: T) -> Option<&i32>;
}
pub enum UserKeys {
Id,
Username,
Password
}
pub struct User {
id: i32,
username: String,
password: String
}
impl Keyable<UserKeys> for User {
fn get_string(&self, key: UserKeys) -> Option<&String> {
match key {
UserKeys::Username => Some(&self.username),
UserKeys::Password => Some(&self.password),
_ => None
}
}
fn get_i32(&self, key: UserKeys) -> Option<&i32> {
match key {
UserKeys::Id => Some(&self.id),
_ => None
}
}
}
This would create a valid implementation of reflection in Rust. It is worth noting that you would not necessarily have to type all of this by hand; you could look into creating a Derive macro (Rust Book).
You could then add a type bound to your EventEmitter so it becomes:
struct EventEmitter<K, T: Keyable<K>> {
pub listeners: Vec<Listener<T>>,
}
This code says "I want to create a struct that can hold many instances of a Listener for type Keyable (T) with a certain key type (K).
There would still be quite a bit of work to do in order to get your events all connected, but taking care of reflection is a big step.
This is an example I've written that allows a derivation of a struct called ToJson. It allows all implementors to automatically inherit a to_json function that creates a String of all its properties. GitHub
I've just learned that an Enum can be initialized with a custom data type.
Wouldn't that make Enums a subset of Structs? An Enum could be a Struct with a single unnamed variable which is initialized once and cannot be changed (like final variables in Java). Also, no methods can be implemented to enums.
Like this:
enum E {
ONE(String)
}
struct S {
one: String
}
Thus, in memory, both a single variable struct and enum would look the same.
Is this true or am I missing something?
It's actually the opposite: structs are subsets of enums. Any struct can be represented as one-variant enum:
struct Record { ... }
struct TupleLike(...);
enum Record { Variant { ... } }
enum TupleLike { Variant(...) }
This enum will even have the same in-memory representation! (though it isn't guaranteed).
On the other hand, enums with multiple variants cannot be described precisely as structs. They are usually implemented as tagged unions, for example:
enum E {
S(String),
I(i32),
}
E::I(123);
type E_Discriminant = u8;
const E_S: E_Discriminant = 0;
const E_I: E_Discriminant = 1;
union E_Payload {
s: String,
i: i32,
}
struct E {
discriminant: E_Discriminant,
payload: E_Payload,
}
E { discriminant: E_I, payload: E_Payload { i: 123 } };
But even doing that manually will not provide you the whole language experience of using enums: you will unable to use pattern matching, accessing variants will be unsafe (and dangerous), etc..
However, when only one variant is needed, structs are more comfortable to use, and that's why they're there.
I'm just learning Rust.
So I know this works:
enum Animal {
Cat { name: String, weight: f64 }
}
fn main() {
let a = Animal::Cat { name: "Spotty".to_string(), weight: 2.7 };
match a {
Animal::Cat{name, weight} => { println!("Cat n={} w={}", name, weight); }
}
}
but why can I not assign from an enum field directly like this:
...
let wt = a.weight;
Or am I using the wrong syntax? Or is it because Rust cannot guarantee an Animal instance will be of type Cat?
Is the only way to access fields of an instance of enum struct or tuple variant by using match?
It is because weight isn't part of the Animal enum, it's part of the Cat type of the enum. Say in the future you add another type of animal that doesn't have weight information:
enum Animal {
Cat { name: String, weight: f64 },
Insect { species: String, height: f64 },
}
Then a.weight wouldn't always have a value. You can use if let or match to conditionally get data from an enum if it exists:
if let Animal::Cat { weight, .. } = a {
println!("Your cat weights {} kg.", weight);
} else {
println!("get a cat");
};
Or is it because Rust cannot guarantee an Animal instance will be of type Cat?
Yes. In a normal enum, you'd have multiple variants, which may or may not have the same types, and thus fields (in fact some may not have fields at all).
So while it would technically be possible for rust to have attribute access on enums with a single variant or where all variants have a compatible version of the attribute, those are such rare cases they don't really matter and the value of such complexity would be essentially nil.
I don't rightly see the point of using a single-variant enum tho, why are you not just using a struct?
struct Cat { name: String, weight: f64 }
?
Is the only way to access fields of an instance of enum struct or tuple variant by using match?
You could also use an if let though that's essentially the same.
Or you can have a method which exposes the information (with a match or if let inside), as convenience (or because the variants are private).
I have a simple struct with one string field:
pub struct Chunk {
signature: String
}
This string field cannot be just any string, there are bunch of constraints that it needs to satisfy (and if string passed to constructor does not satisfy these constraints, construction of struct should fail).
In object-oriented languages like C++, I would make the explicit constructor that does the needed checks, what is the correct way to do this in Rust?
I came up with this code:
impl Chunk {
pub fn new(s: String) -> Option<Chunk> {
if constraints_fail {
None
} else {
Some(Chunk{signature: s})
}
}
}
I'm not sure if this is a correct approach, since technically the struct can still be instantiated with invalid string parameter without ever calling this function.
Your struct contains a private field, therefore it cannot be instantiated from outside. This is what you get if you try:
error[E0451]: field `signature` of struct `Chunk` is private
--> src/main.rs:8:24
|
8 | let _ = Chunk { signature: "xxx".to_owned() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ private field
You don't need to do anything else - just keep the signature field private.
If I try to define a recursive struct/enum in Rust:
enum Enum {
A,
B(Enum),
C(Enum, i32),
D(Enum, Enum),
...
}
I will get a compilation error, as expected.
I know that one possible solution to that problem is to wrap all the recursive references with Box<T> like this:
enum Enum {
A,
B(Box<Enum>),
C(Box<Enum>, i32),
D(Box<Enum>, Box<Enum>),
...
}
or even provide a type alias:
type Enum = Box<InnerEnum>;
enum InnerEnum {
A,
B(Enum),
C(Enum, i32),
D(Enum, Enum),
...
}
This made me wonder if it is possible to do it automatically somehow? I there any macro like this?
#[boxed]
enum Enum {
A,
B(Enum),
C(Enum, i32),
D(Enum, Enum),
...
}
I would strongly recommend to stay with the classic approach of explicitly writing out the whole enum.
But - regarding your question of possibility - I think it can be done (to a certain extent):
macro_rules! boxed_enum{
($dummy: ident, enum $E: ident $($variant:tt)* ) => {
pub mod $dummy {
type $E = Box<$dummy>;
pub enum $dummy $($variant)*
}
type $E = $dummy::$dummy;
}
}
boxed_enum!(InnerEnum, enum Enum {
A,
B(Enum),
C(Enum, i32),
D(Enum, Enum),
});
The macro takes a $dummy, which is both the name of an auxiliary module and an auxiliary enum (as described in your trick), and type-aliases the dummy-enum to your desired identifier.
I think it is possible to generate the $dummy from $E so that the user does not need to specify this explicitly. However, this would rely on concat_idents (iirc, nightly-only) or other crates such as paste.
As said, I'd go with a manual solution in this case.