0

I want to return String from &Option<String> which is returned from config map from configparser::ini::Ini

use configparser::ini::Ini;


fn main() {
    let filename : String = "p2pvpn.conf".to_owned();
    let mut config = Ini::new();
    let map = config.load(filename).unwrap();
    let tunc = map.get("tun").unwrap(); //returns hashmap

    /*
     * 1st unwrap returns &Option<String>
     * 2nd unwrap should return String
     */
    let tunopt : String  = tunc.get("ip").unwrap().unwrap(); //here is the problem
    println!("{tunopt}"); 

}

But I am getting this error:

 --> src/main.rs:9:28
  |
9 |     let tunopt : String  = tunc.get("ip").unwrap().unwrap();
  |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ move occurs because value has type `Option<String>`, which does not implement the `Copy` trait
  |
help: consider borrowing the `Option`'s content
  |
9 |     let tunopt : String  = tunc.get("ip").unwrap().unwrap().as_ref();
  |                                                            +++++++++

I tried that option with as_ref, but it did not helped (expected struct String, found reference), and to_string did not helped too.

I know that first unwrap after get returns &Option<String> tunc.get("ip").unwrap().unwrap().

I tried this:

let tunopt : Option<String>  = *(tunc.get("ip").unwrap()); //move this, deference using asterisk

I thought it will move ownership but still not working

13 |     let tunopt : Option<String>  = *(tunc.get("ip").unwrap()); //move this, deference using asterisk
   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^ move occurs because value has type `Option<String>`, which does not implement the `Copy` trait
   |
help: consider borrowing the `Option`'s content

My questions are:

  1. How to properly get value String from option reference &Option<String>?
  2. Why I can't deference &Option<String> and move it's ownership to variable of type Option<String>?
  3. Why is it complaining about this: not implement the 'Copy' trait', I am tring to move and not to copy.

3 Answers 3

2

You cannot move a String out of a &Option<String>, full stop. In fact, you can't move anything out of an object behind an immutable reference (&), unless the object has some form of interior mutability (RefCell, Mutex, etc). That is the point of immutable borrows.

To move the String out of the config, the config would have to update itself to reflect that it no longer owns the string and that it should no longer attempt to access it. Otherwise, it may very well attempt to access the string after you have freed it, causing memory unsafety.

configparser::ini::Ini does have a method to remove the string value and transfer its ownership: remove_key. This will allow you to move the string out of the Ini, but as the name suggests, it does this by removing it from the Ini object.

Or you could just clone the string. Config parsing is unlikely to be a hotspot in your program, and such strings aren't usually that big.

Sign up to request clarification or add additional context in comments.

2 Comments

I am not moving String from &Option<String>. I tried to deference &Option<String> using * and move it to another Option<String> variable.
@T0maas The reasoning is still the same - you cannot move values out of an immutable reference.
1

You can't directly move a value from a HashMap using get. Moving generally imply that you are taking something from somewhere.

In this case you are trying to simply "get" the value, so you only receive the reverence to it. To move it, you can remove the element from the HashMap, so you receive the value itself.

In practice you can do is:

Copy a value from the hashMap:

let map = config.load(filename).unwrap();
let tunc = map.get("tun").unwrap();
let tunopt: &Option<String> = tunc.get("ip").unwrap();
let tunopt: &String = tunopt.as_ref().unwrap();
let tunopt: String = tunopt.to_owned();

Or take the value, AKA remove from HashMap to local var.

let mut map = config.load(filename).unwrap();
let tunc = map.get_mut("tun").unwrap();
let tunopt: Option<String> = tunc.remove("ip").unwrap();
let tunopt: String = tunopt.unwrap();

1 Comment

It is worth noting, for the beginners, that & and .as_ref() act different depending on the type. As_ref() turns &Option<String> into Option<&String>, which is what I was struggling with.
1

You can't move the String out of the shared reference, but You can borrow the Option's contents by using the reference on that String:

use configparser::ini::Ini;

fn main() {
    let filename: String = "p2pvpn.conf".to_owned();
    let mut config = Ini::new();
    let map = config.load(filename).unwrap();
    let tunc = map.get("tun").unwrap(); //returns hashmap

    /*
     * 1st unwrap returns &Option<String>
     * 2nd unwrap returns &String
     */
    let tunopt: &String = tunc.get("ip").unwrap().as_ref().unwrap(); //problem solved
    println!("{tunopt}");
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.