Beginner Rust fan - I am getting a panic for either one of these.
I expected it from the first part, but not the second.
What am I missing?
fn main() {
//let age = "4a";
//let age2: i32 = age.trim().parse().unwrap();
//println!("{:?}", age2);
let age = "4a";
let age2: i32 = age.trim().parse().expect("What was this?");
println!("{:?}", age2);
}
From expect()'s documentation:
Panics
Panics if the value is an Err, with a panic message including the passed message, and the content of the Err.
The only difference to unwrap() is the custom error message.
See also: When should we use unwrap vs expect in Rust.
Related
In the following code I would like to get the 2nd string in a vector args and then parse it into an i32. This code will not complie, however, because i can not call parse() on the Option value returned by nth().
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
let a = args.iter().nth(1).parse::<i32>();
}
I know i could just use expect() to unwrap the value, before trying to parse it, however I do not want my code to panic. I want a to be a Result value that is an Err if either nth() or parse() fails, and otherwise is a Ok(Int). Is there a way to accomplish this in rust? Thanks.
It is quite easy if you look in the documentation for either Option or Result. The function you are thinking of is likely and_then which allows you to then provide a closure which can change the Ok type and value if filled, but otherwise leaves it unchanged when encountering an error. However, you need to do though is decide on a common error type to propagate. Since the Option<&String> needs to be turned to an error on a None value we have to choose a type to use.
Here I provide a brief example with a custom error type. I decided to use .get instead of .iter().nth(1) since it does the same thing and we might as well take advantage of the Vec since you have gone to the work of creating it.
use std::num::ParseIntError;
enum ArgParseError {
NotFound(usize),
InvalidArg(ParseIntError),
}
let args: Vec<String> = env::args().collect();
let a: Result<i32, ArgParseError> = args
.get(1) // Option<&String>
.ok_or_else(|| ArgParseError::NotFound(1)) // Result<&String, ArgParseError>
.and_then(|x: &String| {
x.parse::<i32>() // Result<i32, ParseIntError>
.map_err(|e| ArgParseError::InvalidArg(e)) // Result<i32, ArgParseError>
});
You could try the following.
use std::{env, num::ParseIntError};
enum Error {
ParseIntError(ParseIntError),
Empty,
}
fn main() {
let args: Vec<String> = env::args().collect();
let a: Option<Result<i32, ParseIntError>> = args.iter().nth(1).map(|s| s.parse::<i32>());
let a: Result<i32, Error> = match a {
Some(Ok(a)) => Ok(a),
Some(Err(e)) => Err(Error::ParseIntError(e)),
None => Err(Error::Empty),
};
}
I am writing code to initialize an array of MaybeUninits and drop all initialized elements in case of a panic. Miri complains about undefined behaviour, which I've reduced to the example below.
use std::mem::{transmute, MaybeUninit};
fn main() {
unsafe {
let mut item: MaybeUninit<String> = MaybeUninit::uninit();
let ptr = item.as_mut_ptr();
item = MaybeUninit::new(String::from("Hello"));
println!("{}", transmute::<_, &String>(&item));
ptr.drop_in_place();
}
}
Error message produced by cargo miri run:
error: Undefined Behavior: trying to reborrow for SharedReadWrite at alloc1336, but parent tag <untagged> does not have an appropriate item in the borrow stack
--> /home/antek/.local/opt/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:179:1
|
179 | pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trying to reborrow for SharedReadWrite at alloc1336, but parent tag <untagged> does not have an appropriate item in the borrow stack
|
As far as I can tell, this is exactly how MaybeUninit is supposed to be used.
Am I using the interface incorrectly and invoking undefined behaviour, or is Miri being overly conservative?
Your issue is actually completely unrelated to MaybeUninit and can be boiled down to:
fn main() {
let mut item = 0;
let ptr = &item as *const _;
item = 1;
// or do anything with the pointer
unsafe { &*ptr; }
}
Playground link
Which throws the same "undefined behavior" error when run with Miri. From what I could gather by reading their reference, it seems like Miri has some metadata that keeps track of which pointers are allowed to read an item, and when you reassign that item, the metadata is wiped. However, reassigning values shouldn't change their memory address, so I would say that this is a bug with Miri and not UB.
In fact, changing ptr to a *mut i32 then using ptr.write instead of the assignment actually gets rid of the UB warning, which means it's probably a false positive.
I would like to forward the output of tokio::process::child::ChildStdout which implements tokio::io::AsyncRead to a futures::channel::mpsc::UnboundedSender<MyType>, which implements futures::sink::Sink.
I am using a custom codec which produces items of MyType, but to stay true to the M in MRE, I will use Tokio's LinesCodec and say that MyType = String for this question.
use futures::StreamExt; // 0.3.8
use tokio; // 1.0.1
use tokio_util; // 0.6.0
#[tokio::main]
pub async fn main() {
let (mut tx, rx) = futures::channel::mpsc::unbounded::<String>();
let mut process = tokio::process::Command::new("dmesg")
.arg("-w")
.stdout(std::process::Stdio::piped())
.spawn()
.unwrap();
let stdout = process.stdout.take().unwrap();
let codec = tokio_util::codec::LinesCodec::new();
let framed_read = tokio_util::codec::FramedRead::new(stdout, codec);
let forward = framed_read.forward(tx);
// read from the other end of the channel
tokio::spawn(async move {
while let Some(line) = rx.next().await {
eprintln!("{}", line);
}
});
//forward.await;
}
However, the compiler is reporting a mismatch in error types:
error[E0271]: type mismatch resolving `<futures::channel::mpsc::UnboundedSender<String> as futures::Sink<String>>::Error == LinesCodecError`
--> src/main.rs:19:31
|
19 | let forward = framed_read.forward(tx);
| ^^^^^^^ expected struct `futures::channel::mpsc::SendError`, found enum `LinesCodecError`
Assuming that I am not doing something fundamentally wrong here, how can I handle/convert these error types properly?
There seems to have been a similar question asked before but it seems to be for the opposite situation and futures 0.1 and I suspect it may be outdated since things are changing so quickly in Rust's async ecosystem.
The items in the stream can fail (LinesCodecError) and sending the value into the channel can fail (SendError), but the whole forwarding process can only result in a single error type.
You can use SinkExt::sink_err_into and TryStreamExt::err_into to convert the errors into a compatible unified type. Here, I've chosen Box<dyn Error>:
type Error = Box<dyn std::error::Error>;
let forward = framed_read.err_into::<Error>().forward(tx.sink_err_into::<Error>());
In many cases, you'd create a custom error type. You also probably wouldn't need to use the turbofish as much as the above example, as type inference will likely kick in at some point.
See also:
How do you define custom `Error` types in Rust?
I'm trying to get into Rust from a Python background and I'm having an issue with a PoC I'm messing around with. I've read through a bunch of blogs and documentation on how to handle errors in Rust, but I can't figure out how to implement it when I use unwrap and get a panic. Here is part of the code:
fn main() {
let listener = TcpListener::bind("127.0.0.1:5432").unwrap();
// The .0 at the end is indexing a tuple, FYI
loop {
let stream = listener.accept().unwrap().0;
stream.set_read_timeout(Some(Duration::from_millis(100)));
handle_request(stream);
}
}
// Things change a bit in here
fn handle_request(stream: TcpStream) {
let address = stream.peer_addr().unwrap();
let mut reader = BufReader::new(stream);
let mut payload = "".to_string();
for line in reader.by_ref().lines() {
let brap = line.unwrap();
payload.push_str(&*brap);
if brap == "" {
break;
}
}
println!("{0} -> {1}", address, payload);
send_response(reader.into_inner());
}
It is handling the socket not receiving anything with set_read_timeout on the stream as expected, but when that triggers my unwrap on line in the loop it is causing a panic. Can someone help me understand how I'm properly supposed to apply a match or Option to this code?
There seems to be a large disconnect here. unwrap or expect handle errors by panicking the thread. You aren't really supposed to "handle" a panic in 99.9% of Rust programs; you just let things die.
If you don't want a panic, don't use unwrap or expect. Instead, pass back the error via a Result or an Option, as described in the Error Handling section of The Rust Programming Language.
You can match (or any other pattern matching technique) on the Result or Option and handle an error appropriately for your case. One example of handling the error in your outer loop:
use std::net::{TcpStream, TcpListener};
use std::time::Duration;
use std::io::prelude::*;
use std::io::BufReader;
fn main() {
let listener = TcpListener::bind("127.0.0.1:5432")
.expect("Unable to bind to the port");
loop {
if let Ok((stream, _)) = listener.accept() {
stream
.set_read_timeout(Some(Duration::from_millis(100)))
.expect("Unable to set timeout");
handle_request(stream);
}
}
}
Note that I highly recommend using expect instead of unwrap in just about every case.
I have tried to create a mutable reference to clone from captured variable. But compiler said:
rustc: /home/rustbuild/src/rust-buildbot/slave/nightly-linux/build/src/llvm/lib/IR/Instructions.cpp:2522:
static llvm::CastInst* llvm::CastInst::CreatePointerCast(llvm::Value*, llvm::Type*, const llvm::Twine&, llvm::Instruction*):
Assertion `S->getType()->isPtrOrPtrVectorTy() && "Invalid cast"' failed.
What am I doing wrong?
fn foo(t: &mut int){
println!("{}", t);
}
fn main() {
let test = 10;
let h = move || {
let mut r = &mut test.clone();
foo(r);
};
h();
}
This is most likely a bug in the compiler which causes LLVM to crash (the assertion happens in LLVM code, not in Rust - otherwise there would be corresponding message and a possibility to get a backtrace). I've submitted a ticket here, you can subscribe to it to follow its progress.