How to use Option in Struct (default / new)

#1

Hello i would like to have a struct initialized with new or default, but somehow i do it wrong, i am now searching the web for hours but could not find anything that helped

My Example Code:

struct MyStruct {
    a: Option<String>,
    b: Option<String>,
    c: Option<String>,
    d: Option<i32>,
    e: Option<String>,
}

impl MyStruct {
  fn new(a: String, b: String, c: String, d: i32, e: String) -> myStruct  {
    a: a,
    b: b,
    c: c,
    d: d,
    e: e,
  }
}

impl Default for MyStruct {
  fn default() -> MyStruct {
    a = "a",
    b = "b",
    c = "c",
    d = 0,
    e = "e",
  }
}

im really new, so i guess i miss eg Some() or something, but I’m stuck…

With kind regards
Marc

0 Likes

#2
struct MyStruct {
    a: Option<String>,
    b: Option<String>,
    c: Option<String>,
    d: Option<i32>,
    e: Option<String>,
}

impl MyStruct {
  fn new(a: String, b: String, c: String, d: i32, e: String) -> MyStruct  {
    MyStruct  {
        a: Some(a),
        b: Some(b),
        c: Some(c),
        d: Some(d),
        e: Some(e),
    }
  }
}

impl Default for MyStruct {
  fn default() -> MyStruct {
    MyStruct {
        a: Some("a".to_string()),
        b: Some("b".to_string()),
        c: Some("c".to_string()),
        d: Some(0),
        e: Some("e".to_string()),
    }
  }
}

alternatively, if you want to have None be the default for the Options in MyStruct you can do

#[derive(Default)]
struct MyStruct {
    a: Option<String>,
    b: Option<String>,
    c: Option<String>,
    d: Option<i32>,
    e: Option<String>,
}

impl MyStruct {
  fn new(a: String, b: String, c: String, d: i32, e: String) -> MyStruct  {
    MyStruct  {
        a: Some(a),
        b: Some(b),
        c: Some(c),
        d: Some(d),
        e: Some(e),
    }
  }
}

Note that string literals have the type &'static str, not String. Because of this, we need to use the to_string function to convert &'static str to a String.

2 Likes

#3

For more about


Summary of links

enum in Rust provide a way to talk about multiple different types while using the one type.

enum Choice {
    Foo(String),
    Bar(i32)
}

fn main() {
    // this is so we don't need to put "Choice::" before everything
    // this is similar to how Option works, only difference is that
    // with Option, this is automatically done.
    use self::Choice::*;

    let foo: Choice = Foo("Hello World".to_string());
    let bar: Choice = Bar(10);

    let a: Choice;

    // magic random bool that you can change as much as you want
    let random_bool = false; 

    if random_bool {
        a = foo; // Here `a` will store a String, which is inside Choice
    } else {
        a = bar; // Here `a` will store an i32, which is inside Choice
    }

    // here we don't know if `a` stores a String or an i32 until we check

    // here we check if we had a Foo variant or a Bar variant
    // in doing so, we figure out if we stored a String or an i32
    match a {
        Foo(s) => println!("We had the String: {}", s),
        Bar(i) => println!("We had the i32: {}", i)
    }
}


note to people well versed in Rust, I know that there is a better way of writing this code, but the better way can be more confusing so I opted for the more clear way.

note to @braindef and anyone else who needs help, if you need more help in the future, feel free to message me

2 Likes

#4

Hello KrishnaSannasi

i already tried this and get the following error:

error: expected one of `!`, `->`, `::`, `;`, or `}`, found `,`
  --> src/main.rs:24:15
   |
24 |     a: Some(a),
   |               ^ expected one of `!`, `->`, `::`, `;`, or `}` here

error: expected type, found `"a"`
  --> src/main.rs:34:13
   |
34 |     a: Some("a".to_string()),
   |             ^^^ expecting a type here because of type ascription
0 Likes

#5

Ahhh, sorry. My bad for not testing the code before posting. Fixed it.

The problem is that you need to wrap the fields in MyStruct { /* fields here */ } to initialize MyStruct.

0 Likes

#6

how to wrap, can you post the code? Im really a beginner, i tried rust 0.5 Years ago, then it frustrated me and i did something else. And now this is just my second try.

0 Likes

#7

What I mean is

MyStruct {
    a: Some("Hi"),
    b: None,
    c: Some("Another Field"),
    // .. for the rest of the fields
}

Will initialize the struct MyStruct with the given fields, and you must provide all of the fields.

0 Likes

#8

That’s how I started!

0 Likes

#9

that makes it even more worse

   Compiling hello_world v0.1.0 
error: expected type, found `"Hi"`
  --> src/main.rs:15:13
   |
15 |     a: Some("Hi"),


struct MyStruct {
    a: Some("Hi"),
    b: None,
    c: Some("Another Field"),
    d: Some(0),
    e: Some("TEST"),
}

in this case the complier does not eve accept my struct definition

0 Likes

#10

That is because you are trying to define a struct MyStruct with values as types.

I was talking about initializing the MyStruct that you gave a definition for at the beginning.

Your definition at the top was correct.

struct MyStruct {
    a: Option<String>,
    b: Option<String>,
    c: Option<String>,
    d: Option<i32>,
    e: Option<String>,
}

to initialize this struct after you have defined it, you can do

MyStruct {
    a: Some("Hi"),
    b: None,
    c: Some("Another Field"),
    d: Some(0),
    e: Some("TEST"),
}

And you can assign it to a variable like so

let foo = MyStruct {
    a: Some("Hi"),
    b: None,
    c: Some("Another Field"),
    d: Some(0),
    e: Some("TEST"),
};
0 Likes

#11

Rust does it this way because we don’t have special constructor functions like in Java or C++, so to initialize a variable you specify all of the values that the fields should have up front.

0 Likes

#12

Ok, this works, but i wanted to have new() or default() becaues i need to initialize many of them and dont want it write all the time, and its also not that good so the code gets massively blown up… something like this here but with an Option type: https://internals.rust-lang.org/t/struct-field-defaults/3412/7

0 Likes

#13

If all of your fields implement Default, then you can mark your struct definition with #[derive(Default)] to automatically get a Default impl for your struct.

like so

#[derive(Default)]
struct MyStruct {
    a: Option<String>,
    b: Option<String>,
    c: Option<String>,
    d: Option<i32>,
    e: Option<String>,
}

Also you can now call your default function like so

let bar = Foo::default();

Note: Option defaults to None, if you want to change that, then you will need to implement Default yourself for your struct.


#[derive(Default)] is an annotation that calls a macro which generates the following code for you

impl Default for MyStruct {
  fn default() -> MyStruct {
    MyStruct {
        a: <Option<String> as Default>::default(),
        b: <Option<String> as Default>::default(),
        c: <Option<String> as Default>::default(),
        d: <Option<i32> as Default>::default(),
        e: <Option<String> as Default>::default(),
    }
  }
}

<Option<String> as Default>::default() is Universal Function Call Syntax or UFCS for short. It allows specifying a function to disambiguate which function you mean if there exists multiple functions with the same name on a type.

2 Likes

#14

ok, this did the trick, thank you for your help :slight_smile:

0 Likes

#15

No problem! :slight_smile:


On a side note, more stuff about this forum

@username will notify the person that you specify that you mentioned them.
i.e @KrishnaSannasi will notify me

for code fences, you can specify rust to get syntax highlighting

```rust

```

and if you highlight someone else’s post, you should see a thing come up with “Quote”, this will allow you to nicely quote other people!

4 Likes

#16

i have a bonus question:

the function lets say findBest() creates this struct, how do i use this struct outside of this function

example


fn main() {

//  let selected_rows = Vec<MyStruct> with sql query, that works already

  let (rowNumber, myStruct, similarity) = findBest(selected_rows, "Test");

}


fn findBest(allRows : Vec<MyStruct>, String : searchedWord) -> (i32, MyStruct, f64)
{
//  doing the processingm, that also already works
for row in &allRows
{
  //processing with strsim library
}

  return (rowNumber, selected_row, similarity)
}

warning: unused imports: `damerau_levenshtein`, `hamming`, `jaro_winkler`, `levenshtein`, `normalized_damerau_levenshtein`, `normalized_levenshtein`, `osa_distance`
 --> src/main.rs:6:14
  |
6 | use strsim::{hamming, levenshtein, normalized_levenshtein, osa_distance,
  |              ^^^^^^^  ^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^^^^^^  ^^^^^^^^^^^^
7 |              damerau_levenshtein, normalized_damerau_levenshtein, jaro,
  |              ^^^^^^^^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
8 |              jaro_winkler};
  |              ^^^^^^^^^^^^

error[E0308]: mismatched types
  --> src/main.rs:98:28
   |
98 |   return ( rowNumber, returnRow, similarity )
   |                       ^^^^^^^^^ expected struct `MyStruct`, found &MyStruct
   |
   = note: expected type `MyStruct`
              found type `&MyStruct`
0 Likes

#17

Well, as the error suggest you have a reference to your struct and not the struct itself; either take a .clone, make it Copy or move the struct.


Clone:

#[derive(Clone)]
struct MyStruct {
    //
}

Copy:

#[derive(Clone, Copy)]
struct MyStruct {
    //
}

To elaborate:

  • Clone will allow you to call .clone on any object which implements clone, which includes a T or a &T
  • Copy will allow you to implicitly copy an object, as is experienced with numerical values; they aren’t usually passed as references, &u32 isn’t very common, instead passing a u32 is more normal
  • Moving an object (literally) takes it out of the original place and gives you an owned value; Vec<YourStruct>::remove(index) will take the object out of the Vec and give it to you.
let mut myvec: Vec<MyStruct> = vec![a, b, c]; //Initialize
let taken_a: MyStruct = myvec.remove(0);
assert_eq!(myvec, vec![b, c]);
assert_eq!(taken_a, a);
  • Or you could always return a reference instead; this would look like this:
fn find_best(all_rows: &Vec<MyStruct>, searched_word: &String) -> (i32, &MyStruct, f64) {
    // Result:
    let mut best_found: &MyStruct;
    // Process
    //...
    (row_number, best_found, similarity)
}
1 Like

#18

Thank you this solved my problem. These directives seem to be important in rust. since im very new in rust is there any good tutorial to to learn about all these directives?

With kind regards and thank you again
Marc

0 Likes

#19

This post by @steveklabnik is points to some good resources.
Actually just checked and the copy resource he points to seems to be out of data, so instead look at this

1 Like