How can I get the value of a struct which is returned in a Result from another function? Example below.
#[derive(Debug)]
pub struct Keypair(ed25519_dalek::Keypair);
pub fn keypair_from_seed(seed: &[u8]) -> Result<Keypair, Box<dyn error::Error>> {
let dalek_keypair = ed25519_dalek::Keypair { secret, public };
Ok(Keypair(dalek_keypair))
}
fn main(){
//here seed_bytes is mnemonics
let sk = keypair_from_seed(&seed_bytes);
//sk contains the secret key and public key, i want to extract it different secret key & public key
}
If you feel very confident
let sk = keypair_from_seed(&seed_bytes).unwrap();
or
let sk = keypair_from_seed(&seed_bytes).expect("my own failure message");
However, it is recommended to proceed like this
if let Ok(sk) = keypair_from_seed(&seed_bytes) {
// ... use sk ...
} else {
// ... sk is not available, may be should
// we warn the user, ask for an alternative ...
}
or, if you want to explicitly handle the error
match keypair_from_seed(&seed_bytes) {
Ok(sk) => {
// ... use sk ...
},
Err(e) => {
// ... sk is not available, and e explains why ...
},
}
Note that, if the function containing these lines is also
able to return an error, you can just propagate it with
the ? notation (if the error returned by
keypair_from_seed() is convertible into the error returned
by your function)
let sk = keypair_from_seed(&seed_bytes)?;
see
unwrap,
expect,
if let,
match,
?
Lets look the definition of Result in Rust documentation
enum Result<T, E> {
Ok(T),
Err(E),
}
So a Result is either Ok which contains a value with type T, or Err which contains a value with type E.
You have couple options to extract the value.
1- result.unwrap() or result.expect("error message")
This function returns the Ok value if result is Ok or panics the program (program is terminated). If you are sure that it doesn't contain error or you just want to write the correct case first and deal with error handling later it makes sense but you shouldn't use it all the time since it directly crashes the app when the value is not Ok.
You can use it like this
let val = result.unwrap();
// or
let val = result.expect("oops not Ok");
Only difference of expect you can provide the error message yourself instead of the standard error message of unwrap.
2- Pattern matching
In Rust, pattern matching is used for enum types so that user can do the necessary thing based on the current variant of the enum. You can use it like this
match result {
Ok(val) => {
// Use val here....
},
Err(err) => {
// Do something with the error if you want
}
}
If you are going to handle only one variant, you can also use if let statement like this
if let Some(val) = result {
// Do something with val
}
The returned result from the function is of the type Result<Keypair, Box<dyn error::Error>>.
There are multiple ways to extract a result from the Result container. Basically rust wants you to check for any errors and handle it. If no errors, you can extract the result and use it.
if let Ok(sk) = keypair_from_seed(&seed) {
let public = sk.0.public;
let secret = sk.0.secret;
/* use your keys */
}
Notice the sk.0 since you are using a struct of a tuple type. If your struct had multiple variables, something like
pub struct KeyTuple(ed25519_dalek::Keypair, i32, &str);
You would have used it as
let kt = keytuple_from_seed(&seed).unwrap();
let kp: ed25519_dalek::Keypair = kt.0;
let le: i32 = kt.1;
let st: &str = kt.2;
Related
I would like to know if there's any elegant solution for getting code/behaviour similar to unwrap_or_else on an Option<&T>. My use case is to pass an optional reference to a function and if it's not used then create a default value of the same type to use. Here's a boiled-down version of my code:
#[derive(Debug)]
struct ExpensiveUnclonableThing {}
fn make_the_thing() -> ExpensiveUnclonableThing {
// making the thing is slow
// ...
ExpensiveUnclonableThing {}
}
fn use_the_thing(thing_ref: &ExpensiveUnclonableThing) {
dbg!(thing_ref);
}
fn use_or_default(thing_ref_opt: Option<&ExpensiveUnclonableThing>) {
enum MaybeDefaultedRef<'a> {
Passed(&'a ExpensiveUnclonableThing),
Defaulted(ExpensiveUnclonableThing),
}
let thing_md = match thing_ref_opt {
Some(thing_ref) => MaybeDefaultedRef::Passed(thing_ref),
None => MaybeDefaultedRef::Defaulted(make_the_thing()),
};
let thing_ref = match &thing_md {
MaybeDefaultedRef::Passed(thing) => thing,
MaybeDefaultedRef::Defaulted(thing) => thing,
};
use_the_thing(thing_ref);
}
fn use_or_default_nicer(thing_ref_opt: Option<&ExpensiveUnclonableThing>) {
let thing_ref = thing_ref_opt.unwrap_or_else(|| &make_the_thing());
use_the_thing(thing_ref);
}
fn main() {
let thing = make_the_thing();
use_or_default(Some(&thing));
use_or_default(None);
use_or_default_nicer(Some(&thing));
use_or_default_nicer(None);
}
The thing is dropped right away when the unwrap_or_else closure ends, so I of course get an error stating that I can't do that:
error[E0515]: cannot return reference to temporary value
--> src/main.rs:31:53
|
31 | let thing_ref = thing_ref_opt.unwrap_or_else(|| &make_the_thing());
| ^----------------
| ||
| |temporary value created here
| returns a reference to data owned by the current function
What is the 'idiomatic Rust' way of writing use_or_default? Is there a way I can get it to look similar to how use_or_default_nicer is implemented other than by creating a generic MaybeDefaultedRef<T> type + with some convenience methods? I am open to refactoring the whole thing if there's a better way.
You can write something like this:
fn use_or_default_nicer(thing_ref_opt: Option<&ExpensiveUnclonableThing>) {
let mut maybe = None;
let thing_ref = thing_ref_opt.unwrap_or_else(
|| maybe.insert(make_the_thing())
);
use_the_thing(thing_ref);
}
That is, you can keep the value itself outside of the function and then assign to it if necessary. Unfortunately, an unitialized value cannot be capture by a lambda so you have to make the variable Option<ExpensiveUnclonableThing> and initialize with None.
But in a real code of mine, I had the same issue and I wrote a manual match:
fn use_or_default_nicer(thing_ref_opt: Option<&ExpensiveUnclonableThing>) {
let maybe;
let thing_ref = match thing_ref_opt {
Some(x) => x,
None => {
maybe = make_the_thing();
&maybe
}
};
use_the_thing(thing_ref);
}
In my opinion this is nicer even if a bit longer, because you don't need the Option<_> or the maybe variable being mutable` or the fake initialization.
Some people feel a bit of a defeat when they match on an Option, and think it is un-idiomatic, but I don't particularly care.
A plain old if/else would do also, no need to convolute things:
fn use_or_default_nicer(thing_ref_opt: Option<&ExpensiveUnclonableThing>) {
if let Some(e) = thing_ref_opt {
use_the_thing(e);
} else {
let e = make_the_thing();
use_the_thing(&e);
}
}
Playground
I want to move a value into a tuple-type enum variant and obtain a reference to the value after it has been moved. I see how this is possible with an if let statement, but this seems like this should be unnecessary when the particular variant is known statically.
Is there any way to get the reference to the moved value without requiring an if let or match?
This code block is a simple illustration of my question (see below for a more challenging case):
enum Transport {
Car(u32), // horsepower
Horse(String), // name
}
fn do_something(x: &String) {
println!(x);
}
fn main() {
// Can I avoid needing this if, which is clearly redundant?
if let Transport::Horse(ref name) = Transport::Horse("daisy".into()) {
do_something(name);
}
else {
// Can never happen
}
// I tried the following, it gives:
// "error[E0005]: refutable pattern in local binding: `Car(_)` not covered"
let Transport::Horse(ref name) = Transport::Horse("daisy".into());
}
It is easy to find ways to side-step the issue in the above code, since there are no real interface requirements. Consider instead the following example, where I am building a simple API for building trees (where each node can have n children). Nodes have an add_child_node method returning a reference to the node that was added, to allow chaining of calls to quickly build deep trees. (It is debatable whether this is a good API, but that is irrelevant to the question). add_child_node must return a mutable reference to the contents of an enum variant. Is the if let required in this example (without changing the API)?
struct Node {
children: Vec<Child>,
// ...
}
enum Child {
Node(Node),
Leaf
}
impl Node {
fn add_child_node(&mut self, node: Node) -> &mut Node {
self.children.push(Child::Node(node));
// It seems like this if should be unnecessary
if let Some(&mut Child::Node(ref mut x)) = self.children.last() {
return x;
}
// Required to compile, since we must return something
unreachable!();
}
fn add_child_leaf(&mut self) {
// ...
}
}
No. You can use unreachable!() for the else case, and it's usually clear even without message/comment what's going on. The compiler is also very likely to optimize the check away.
If the variants have the same type you can implement AsRef and use the Transport as a &str:
enum Transport {
Car(String),
Horse(String),
}
fn do_something<S: AsRef<str>>(x: &S) {
println!("{}", x.as_ref());
}
impl AsRef<str> for Transport {
fn as_ref(&self) -> &str {
match self {
Transport::Car(s) => s,
Transport::Horse(s) => s,
}
}
}
fn main() {
let transport = Transport::Horse("daisy".into());
do_something(&transport)
}
Playground
Otherwise you need to use a let if binding as you are doing. No need to use an else clause if you don't want to:
if let Transport::Horse(ref name) = Transport::Horse("daisy".into()) {
do_something(name);
}
define From<Transport> for String:
…
impl From<Transport> for String {
fn from(t: Transport) -> String {
match t {
Transport::Car(value) => value.to_string(),
Transport::Horse(name) => name,
}
}
}
fn do_something(x: Transport) {
println!("{}", String::from(x));
}
fn main() {
let horse = Transport::Horse("daisy".to_string());
let car = Transport::Car(150);
do_something(horse);
do_something(car);
}
For a toy example, say I'd like to make a cache for files on disk and log its output. Here's what I hoped would work:
struct Cache {
data: HashMap<String, String>
}
impl Cache {
fn load(&mut self, filename: &str) -> String {
let result = match self.data.get(filename) {
Some(s) => s.clone(),
None => {
let s = fs::read_to_string(filename).expect("couldn't read");
self.data.insert(String::from(filename), s.clone());
return s;
}
};
println!("my result: {}", result);
return result;
}
}
But the None => {...} clause isn't executed like its own function, so its return exits the whole load function. I tried a couple different ways, but couldn't get multi-line match clauses to return a value. Is there any way to get this type of match working in rust?
rust version: 1.50.0
language edition: 2018
The final statement in a block is returned, that applies to any block, i.e. if you put s as the last line in the None match arm, it will be returned from that block.
You only need to use return when returning from a function or closure.
In rust, the following function is legal:
fn unwrap<T>(s:Option<T>) -> T {
s.unwrap()
}
It takes ownership of s, panics if it is a None, and returns ownership of the contents of s (which is legal since an Option owns its contents).
I was trying to write a similar function with signature
fn unwrap_set<T>(s: BTreeSet<T>) -> T {
...
}
The idea is that it panics unless s has size 1, in which case it returns the single element. It seems like this should be possible for the same reason unwrap is possible, however none of the methods on BTreeSet had the right signature (they would need to have return type T). The closest was take, and I tried to do
let mut s2 = s;
let v: &T = s2.iter().next().unwrap();
s2.take(v).unwrap()
but this failed.
Is writing a function like unwrap_set possible?
The easiest way to do this would be to use BTreeSet<T>'s implementation of IntoIterator, which would allow you to easily pull owned values out of the set one at a time:
fn unwrap_set<T>(s: BTreeSet<T>) -> T {
let mut it = s.into_iter();
if let Some(first) = it.next() {
if let None = it.next() {
return first;
}
}
panic!("set must have a single value");
}
If you wanted to indirectly rely on IntoIterator you could also use a normal loop, but I don't think it's as readable that way so I probably wouldn't do this:
fn unwrap_set<T>(s: BTreeSet<T>) -> T {
let mut result = None;
for item in s {
// If there is a second value, bail out
if let Some(_) = result {
result = None;
break;
}
result = Some(item);
}
return result.expect("set must have a single value");
}
Below there is code in which I have defined a method with an input type of Vec<Food>. This method should validate if an arm, without checking the associated value, must be unique. It means it should contain at most 1 pizza, 1 cake and 1 subway. Note: it is not needed that all arms are in the Vec. I wrote some tests in the code below also, they still need to pass.
I have much more enum arms in my 'real' code, and my current way doesn't scale very well, so I was hoping there is a easier way.
fn main() {
}
enum Food {
Cake(String),
Pizza(i32),
Subway(u64)
}
struct CustomError;
fn validate(foods: Vec<Food>) -> Result<(), CustomError> {
let mut cake = false;
let mut pizza = false;
let mut subway = false;
for f in foods.iter() {
match f {
Food::Cake(_) => {
if cake {
return Err(CustomError)
}
cake = true;
},
Food::Pizza(_) => {
if pizza {
return Err(CustomError)
}
pizza = true;
},
Food::Subway(_) => {
if subway {
return Err(CustomError)
}
subway = true;
},
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
assert!(validate(vec![Food::Pizza(1)]).is_ok());
assert!(validate(vec![Food::Pizza(1), Food::Cake("Apple".to_owned())]).is_ok());
assert!(validate(vec![]).is_ok());
assert!(validate(vec![Food::Pizza(1), Food::Pizza(1)]).is_err());
assert!(validate(vec![Food::Pizza(1), Food::Pizza(2)]).is_err());
}
}
To compare enum variants without caring about any associated data, the function std::mem::discriminant is very useful. Given a value of an enum type, std::mem::discriminant returns a value of type std::mem::Discriminant which tells which variant the value is. std::mem::Discriminant implements Hash, so we can keep all the variants we've seen so far in a HashSet to check if there are any duplicates.
Just a small trick: HashSet::insert returns a boolean which is true when the inserted element isn't already in the set. That means we can combine the steps of checking if a discriminant has been seen and inserting a new discriminant.
use std::collections::HashSet;
use std::mem::discriminant;
enum Food {
Cake(String),
Pizza(i32),
Subway(u64),
}
struct CustomError;
fn validate(foods: Vec<Food>) -> Result<(), CustomError> {
let mut discriminants = HashSet::new();
for food in foods {
if !discriminants.insert(discriminant(&food)) {
return Err(CustomError);
}
}
Ok(())
}
(playground)
Rust doesn't have much by way of reflection capabilities, so I suspect the best you can do is write something like a procedural macro to generate a function containing the match statement which you described not wanting to write by hand.