Pass a temporary/local variable to Err [duplicate] - rust

This question already has answers here:
Proper way to return a new string in Rust
(2 answers)
Return local String as a slice (&str)
(7 answers)
How do you define custom `Error` types in Rust?
(3 answers)
What is the most idiomatic way to merge two error types?
(2 answers)
Closed 2 years ago.
I have this function that sends an HTTP request parses the response and returns a Result:
pub fn get_network_info(&self) -> Result<NetworkInfoResult, &'static str> {
let rpc = JsonRpc::new(
String::from("1.0"),
self.settings.id.clone(),
String::from("getnetworkinfo"),
Vec::new(),
);
let url = format!("http://{}:{}", self.settings.url, self.settings.port);
let req = HttpRequest::new_json_rpc(
url,
rpc,
self.settings.user.clone(),
self.settings.pass.clone(),
);
match req.execute() {
Ok(x) => {
println!("{}", x.content);
let parsed: NetworkInfo = serde_json::from_str(&x.content)
.expect("Failed to parse networkinfo Json response");
if parsed.id != self.settings.id {
Err("RPC Request and Response id didn't match!")
} else {
if parsed.error.is_some() {
Err(&parsed.error.unwrap())
} else {
Ok(parsed.result)
}
}
}
Err(e) => Err(e),
}
}
If the error field is set in the JSON response we want to return an error, in particular the error message inside the response. Otherwise, we return the result field of the parsed response.
The problem which this code is that Err(&parsed.error.unwrap()) doesn't compile. The reason for this is that the lifetime of the variable containing the parsed.error.unwrap() is only within the function, therefore it might not exist anymore in the context of the caller.
My problem is that Err expects a reference to a string, but whatever reference I try to pass would be to a local or temporary variable. How do I work around this issue?

My problem is that Err expects a reference to a string, but whatever reference I try to pass would be to a local or temporary variable. Can anybody help me work around this issue?
The "workaround" is to fix the function's signature. Either return a full-blown String, or if you really need the efficiency return a Cow (so you can return either a String or an &'static str). Since parsed is a local you can't return a reference to it.

Related

Why does a value created inside a function borrow and how can I avoid this pattern?

I'm new to rust but an engineer of over 6 years in various other languages from Javascript to Go.
I'm wondering why here the value is borrowed when I convert the response body to an "object".
I understand that the function owns the value and then the value is destroyed when the function returns BUT functions exist to create and return values. So there's clearly something fairly big I'm missing here. Can someone set me straight?
let response = match self
.client
.index(IndexParts::IndexId(index, id))
.body(json!({
"index": index,
"body": doc,
}))
.send()
.await
{
Ok(response) => response,
Err(err) => {
return Err(Box::new(err));
}
};
let response_body = match response.json::<Value>().await {
Ok(response_body) => response_body,
Err(err) => {
return Err(Box::new(err));
}
};
let response_map = response_body.as_object();
Ok(response_map)
I understand that the function owns the value and then the value is destroyed when the function returns BUT functions exist to create and return values. So there's clearly something fairly big I'm missing here.
You need to return an owned value, not a reference into a local. I assume what you're doing now boils down to:
fn foo() -> &Map<String, Value> {
let x = serde_json::json!({}); // except you get it by http
x.as_object().unwrap() // except you do proper error handling
}
This doesn't compile because you're returning the reference to a local value. Instead, you need to return the value itself:
fn foo() -> Map<String, Value> {
let x = serde_json::json!({}); // except you get it by http
match x {
Value::Object(o) => o,
_ => unreachable!(), // you'd return Err(...)
}
}
But even this is more complicated than you need. Since you already deserialize the value yourself, and handle the errors, you can simply ask serde to deliver a Map<String, Value> to begin with:
let response_body = match response.json::<Map<String, Value>>().await {
Ok(response_body) => response_body,
Err(err) => ...
};
Of course, you'll also need to adjust the return type to return the actual value instead of a reference.

Remove "Some" keyword from string println! in rust [duplicate]

This question already has answers here:
How can I pull data out of an Option for independent use?
(3 answers)
Closed 7 months ago.
I want to print out the value of sys.host_name()
let hostname = sys.host_name();
println!("{:?}", hostname);
Output: "Some("arch")"
Expected output: "arch"
How can i print the "raw" string?
It appears (not sure what library you are using) sys.host_name() does not return a string, rather an Option<String>, which implies the function could fail (and return None instead of Some("arch").
println! is printing the full type. If you just wish to print the contained string, you need to extract the string from the Option. One possible way:
let possible_hostname = sys.host_name();
// Check if the hostname is Some or None
if let Some(hostname) = possible_hostname {
println!("{:?}", hostname);
}
else {
println!("hostname could not be determined");
}
If you would prefer your program to just panic if the hostname is None, you can "unwrap" the option:
let hostname = sys.host_name().unwrap();
println!("{:?}", hostname);

How to use mail filter context data?

I am trying to write a mail filter in Rust using the milter crate. I built the example on a Linux VM and it all works fine. However, the example is using u32 as the type of context injected into their handlers, a quite simple example. I instead need to store a string from the handle_header callback through to the handle_eom handler so I can use an incoming header to set the envelope from.
If I log the value of the header in handle_header to console, it writes correctly but by the time it arrives in handle_eom, it has been corrupted/overwritten whatever. I thought that context was supposed to be specifically for this scenario but it seems weird that it uses type inference rather than e.g. a pointer to an object that you can just assign whatever you want to it.
Is my understanding of context wrong or is the code incorrect?
I tried using value and &value in handle_header and it behaves the same way.
use milter::*;
fn main() {
Milter::new("inet:3000#localhost")
.name("BounceRewriteFilter")
.on_header(header_callback)
.on_eom(eom_callback)
.on_abort(abort_callback)
.actions(Actions::ADD_HEADER | Actions::REPLACE_SENDER)
.run()
.expect("milter execution failed");
}
#[on_header(header_callback)]
fn handle_header<'a>(mut context: Context<&'a str>, header: &str, value: &'a str) -> milter::Result<Status> {
if header == "Set-Return-Path" {
match context.data.borrow_mut() {
Some(retpath) => *retpath = &value,
None => {
context.data.replace(value)?;
}
}
}
Ok(Status::Continue)
}
#[on_eom(eom_callback)]
fn handle_eom(mut context: Context<&str>) -> milter::Result<Status> {
match context.data.take() {
Ok(result) => {
println!("Set-return-path header is {}", result.unwrap());
context.api.replace_sender(result.unwrap(), None::<&str>)?;
}
Err(_error) => {}
}
Ok(Status::Continue)
}
Thanks to glts on Github, the author of the crate, the problem was that the string slices passed into the handle_header method were not borrowed by the external code that stores the data pointer so by the time that handle_eom is called, the memory has been reused for something else.
All I had to do was change Context<&str> to Context<String> and convert the strings using mystr.to_owned() and in the reverse direction val = &*mystring

Why is clone required to be called explicitly with Strings in some cases but not others?

I was working in coding dojo trying to learn Rust. In the attached link is all our code and test. However, we got stumped as to why we required calling clone() in one function but not the other.
Why do I need to call game.clone() on line 23 of lib.rs in this link https://cyber-dojo.org/kata/edit/WvEB5z
pub fn say_game_score(game: Game) -> String {
if game.player1.score == game.player2.score {
return say_equal_score(game.player1.score);
}
if can_be_won(game) { // This line required game.clone() WHY???
return say_winning_situation(game); // This line does NOT require game.clone()
}
return format!(
"{} {}",
say_score_name(game.player1.score),
say_score_name(game.player2.score)
);
}
fn say_winning_situation(game: Game) -> String {
if game.player1.score > game.player2.score {
return say_leading_situation(game.player1.name, game.player1.score - game.player2.score);
} else {
return say_leading_situation(game.player2.name, game.player2.score - game.player1.score);
}
}
fn can_be_won(game: Game) -> bool {
return game.player1.score > FORTY || game.player2.score > FORTY;
}
can_be_won(game) causes the variable game to be moved into the function. When you then call say_winning_situation(game) the variable has already moved and cant be used anymore. The Rust compile can actually check these things.
The compiler suggests that you clone the game in the first invocation, so it will be copied instead of moved.
You probably want to use references instead of values in your functions. Only take ownership when you need it. For reading access a reference (which is const by default) is your first choice.
You should read about borrow checking in Rust.

How to access as_secs in SystemTime? "no method named `as_secs` found for enum Result" [duplicate]

This question already has answers here:
Unable to read file contents to string - Result does not implement any method in scope named `read_to_string`
(2 answers)
How can I get the current time in milliseconds?
(7 answers)
Closed 2 years ago.
I'm using std::time::SystemTime. My goal is to make a struct with a field called timestamp and store the time in seconds.
I saw this example which works correctly:
use std::time::SystemTime;
match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
Ok(n) => println!("1970-01-01 00:00:00 UTC was {} seconds ago!", n.as_secs()),
Err(_) => panic!("SystemTime before UNIX EPOCH!"),
}
When I try this code I get an error:
use std::time::SystemTime;
let n = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH);
println!("{}", n.as_secs());
error[E0599]: no method named `as_secs` found for enum `std::result::Result<std::time::Duration, std::time::SystemTimeError>` in the current scope
--> src/main.rs:5:22
|
5 | println!("{}", n.as_secs());
| ^^^^^^^ method not found in `std::result::Result<std::time::Duration, std::time::SystemTimeError>`
What am I doing wrong?
Read the error:
no method named `...` found for type `Result<...>`
So, we look at Result:
Result is a type that represents either success (Ok) or faliure (Err)
See the std::result module for documentation details.
So, we know that SystemTime::duration_since(&self, _) returns a Result, meaning it could possibly have failed. Reading the docs:
Returns an Err if earlier is later than self, and the error contains how far from self the time is.
So, we just have to unwrap, expect, or match on it to get the possibility of an error out:
use std::time::SystemTime;
// Unwrapping
let n = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)
.unwrap(); // Will panic if it is not `Ok`.
// Expecting
let n = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)
.expect("Invalid time comparison"); // Will panic with error message
// if it is not `Ok`.
// Matching
let n = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH);
match n {
Ok(x) => { /* Use x */ },
Err(e) => { /* Process Error e */ },
}
// Fallibly Destructuring:
let n = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH);
if let Ok(x) = n {
/* Use x */
} else {
/* There was an error. */
}

Resources