Extend the lifetime of a temporary variable outside its block - rust

In Rust I sometimes find myself wanting to write code like this:
fn read_possibly_compressed_data(
mut reader: impl Read,
) -> Result<()> {
let is_compressed = reader.read_u8()? == 1;
let reader: &mut dyn Read = if is_compressed {
&mut ZlibDecoder::new(reader)
} else {
&mut reader
};
...
Unfortunately this doesn't work for obvious reasons - the lifetime of ZlibDecoder::new() only extends to the end of its block so I can't return a reference to it.
The obvious solution is to put it outside the block, but then you have to do some initialisation, or maybe put it in an Option<>:
fn read_possibly_compressed_data(
mut reader: impl Read,
) -> Result<()> {
let is_compressed = reader.read_u8()? == 1;
let mut zlib_decoder = ZlibDecoder::new(Cursor::default());
let reader: &mut dyn Read = if is_compressed {
zlib_decoder = ZlibDecoder::new(reader);
&mut zlib_decoder
} else {
&mut reader
};
...
This is slightly less optimal than if the first code just worked. Is there a better way that I'm missing

I found the answer while writing this, guided by Rust's excellent error messages. You can just declare the variable outside the block and assuming the flow control is right you don't have to initialise it:
fn read_possibly_compressed_data(
mut reader: impl Read,
) -> Result<()> {
let is_compressed = reader.read_u8()? == 1;
let mut zlib_decoder;
let reader: &mut dyn Read = if is_compressed {
zlib_decoder = ZlibDecoder::new(reader);
&mut zlib_decoder
} else {
&mut reader
};
...

Related

rust how to make a variable has different types in different conditions

fn main() -> std::io::Result<()> {
let path = std::env::args().nth(1).expect("usage: minidecaf <input path>");
let input = std::fs::read_to_string(path)?;
let output_path = std::env::args().nth(2);
let writer;
if output_path.is_none() {
writer = std::io::stdout();
} else {
writer = std::fs::File::open(output_path.unwrap()).unwrap();
}
minidecaf::run(&input, &mut writer)
}
writer should be stdout() when output_path command line argument is not provided and be File when output_path command line argument is provided.
This code does not work, because the type of writer cannot be determined in compile time.
I change it to the following. Since both File and Stdout implements std::io::Write, and what I need is a reference to std::io::Write. As the signature of function run is:
pub fn run(input: &str, output: &mut impl std::io::Write) -> std::io::Result<()>
fn main() -> std::io::Result<()> {
let path = std::env::args().nth(1).expect("usage: minidecaf <input path>");
let input = std::fs::read_to_string(path)?;
let output_path = std::env::args().nth(2);
let mut writer : &mut std::io::Write;
if output_path.is_none() {
writer = &mut std::io::stdout();
} else {
writer = &mut std::fs::File::open(output_path.unwrap()).unwrap();
}
minidecaf::run(&input, &mut writer)
}
But it does not work, too. It has lifetime problems.
Then I change it to:
fn main() -> std::io::Result<()> {
let path = std::env::args().nth(1).expect("usage: minidecaf <input path>");
let input = std::fs::read_to_string(path)?;
let output_path = std::env::args().nth(2);
minidecaf::run(&input, &mut (if output_path.is_none() {std::io::stdout()} else {std::fs::File::open(output_path.unwrap()).unwrap()}) )
}
It has the same problem with the first piece of code.
error[E0308]: `if` and `else` have incompatible types
--> src/main.rs:11:83
|
11 | minidecaf::run(&input, &mut (if output_path.is_none() {std::io::stdout()} else {std::fs::File::open(output_path.unwrap()).unwrap()}) )
| ----------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Stdout`, found struct `File`
| |
| expected because of this
So how can I make writer be stdout() when output_path command line argument is not provided and be File when output_path command line argument is provided?
As you noted, you need writer to be some kind of reference to std::io::Write, but you also need someone to own the corresponding value. The obvious solution here is to use a Box:
fn run(input: &str, output: &mut impl std::io::Write) -> std::io::Result<()> {
unimplemented!();
}
fn main() -> std::io::Result<()> {
let path = std::env::args()
.nth(1)
.expect("usage: minidecaf <input path>");
let input = std::fs::read_to_string(path)?;
let output_path = std::env::args().nth(2);
let mut writer: Box<dyn std::io::Write>;
if output_path.is_none() {
writer = Box::new(std::io::stdout());
} else {
writer = Box::new(std::fs::File::open(output_path.unwrap()).unwrap());
}
run(&input, &mut writer)
}
Playground
I think you may want to read more about how to use trait.
Here is a sample code may do what you want:
#[derive(Default)]
struct A {
a: usize,
}
#[derive(Default)]
struct B {
b: usize,
}
trait C {
fn test(&self);
}
impl C for A {
fn test(&self) {
println!("A");
}
}
impl C for B {
fn test(&self) {
println!("B");
}
}
fn main() {
let t: Box<dyn C> = if true {
Box::new(A::default())
} else {
Box::new(B::default())
};
t.test();
}
You can try it Here
To avoid the overhead of dynamic dispatch, it is possible to use the Either type from the either crate to wrap the value, which automatically implements all (most?) traits from the standard library if possible:
use {
either::Either,
std::{fs::File, io},
};
let mut writer = match output_path {
Some(path) => Either::Left(File::open(path)?),
None => Either::Right(io::stdout()),
};
(playground)
Here, using a match expression is conceptually clearer in my opinion.
Without using external crates, it is also possible to mimic the behavior of an Either enum using variables:
use std::{env, fs::File, io};
let (mut stdout, mut file);
let writer: &mut dyn io::Write = match output_path {
Some(path) => {
file = File::open(path)?;
&mut file
}
None => {
stdout = io::stdout();
&mut stdout
}
};
(playground)

Rust async-std strange syntax in order to resolve the conflict of read-write borrowing

I am trying to write a tcp server with async-std, in order to resolve the confilice of read-write borrow check, I found a strange way:
use async_std::prelude::*;
use async_std::task;
use async_std::net::TcpListener;
use async_std::io::BufReader;
fn main() {
task::block_on(async {
let listener = TcpListener::bind("0.0.0.0:9000").await.unwrap();
let mut incoming = listener.incoming();
while let Some(stream) = incoming.next().await {
let stream = stream.unwrap();
println!("Client Addr: {:?}", stream.peer_addr().unwrap());
task::spawn( async move {
let (reader, mut writer) = (&stream, &stream); // Stange here <-----
let reader = BufReader::new(reader);
let mut lines = reader.lines();
while let Some(line) = lines.next().await {
let mut line = line.unwrap();
line.push('x');
line.push('\n');
writer.write_all(line.as_bytes()).await.unwrap();
}
});
}
});
}
Strange:
let (reader, mut writer) = (&stream, &stream); // Stange here <-----
I wrote a verification program following this method:
fn main() {
let mut arr = vec![1, 2, 3, 4];
let (r, mut w) = (&arr, &arr);
for v in r.iter() {
if v == &2 {
w.push(5);
}
}
dbg!(arr);
}
got error:
error[E0596]: cannot borrow `*w` as mutable, as it is behind a `&` reference
can anyone explain it ?
This works because TcpStream has the following impls:
impl Read for TcpStream
impl Write for TcpStream
impl<'_> Read for &'_ TcpStream
impl<'_> Write for &'_ TcpStream
The first two are the "normal" ones, the last two are the "special" ones that allow you to do this reader/writer trick.
For example: The read() trait method is defined like this:
pub fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> ImplFuture<Result<usize>>
When you do stream.read(buf), you're using the impl Read for TcpStream impl. Therefore, you're calling the method with self being of type &mut TcpStream.
When you do let reader = &stream; reader.read(buf), you're using the impl<'_> Read for &'_ TcpStream. Therefore, you're calling the method with self being of type &mut &TcpStream.

Rust same mutable reference in multiple vectors

I am trying to store an object into 2 different reference vectors, and if I modify the object from first vector ref, it should be visible from the second vector.
I still haven't understood well borrowing so that's what I'm trying to acheive :
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Result;
use std::vec::Vec;
struct A {
x: u32,
}
impl Display for A {
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "{}", self.x)
}
}
fn main() {
let mut a = A{x: 1};
let mut v1: Vec<&mut A> = Vec::new();
let mut v2: Vec<&mut A> = Vec::new();
v1.push(&mut a);
v2.push(&mut a);
let f: &mut A = v1[0];
f.x = 3;
for v in &v1 {
println!("Value: {}", v);
}
for v in &v2 {
println!("Value: {}", v);
}
}
Of course this doesn't compile. What should I do to be able to store same object in different collection like objects ? (I don't see any concurency issue or borrowing here).

Read reference from Option<&mut T> multiple times

I have an Option<&mut T> and want to access the contained reference multiple times, like so:
fn f(a: Option<&mut i32>) {
if let Some(x) = a {
*x = 6;
}
// ...
if let Some(x) = a {
*x = 7;
}
}
fn main() {
let mut x = 5;
f(Some(&mut x));
}
That doesn't work, because if let Some(x) = a moves the reference value out of the Option, and the second if let Some(x) = a will result in a compiler error. Without the second if let ..., this works flawlessly, so a doesn't have to be mutable.
The following:
if let Some(ref x) = a {
**x = 6;
}
gives an error: "assignment into an immutable reference".
This would work:
fn f(mut a: Option<&mut i32>) {
if let Some(ref mut x) = a {
**x = 6;
}
if let Some(ref mut x) = a {
**x = 7;
}
}
The mut a is necessary, otherwise I get an error "cannot borrow immutable anonymous field (a:std::prelude::v1::Some).0 as mutable". But this feels wrong: a shouldn't have to be mutable, because I'm not modifying it (see above).
What's the correct solution?
Edit 1
My problem is different from the one in How to pass `Option<&mut ...>` to multiple function calls without causing move errors?. I want to mutably dereference the reference in an Option<&mut T> multiple times, while the other one wants to pass an Option to multiple function invocations. The solutions to the other question are not applicable to my situation.
What about this?
fn f(a: Option<&mut i32>) {
if let Some(&mut ref mut x) = a {
*x = 6;
}
// ...
if let Some(&mut ref mut x) = a {
*x = 7;
}
}
In this case, a doesn't need to be mutable.
The &mut ref mut feels a bit awkward, but it makes sense: first we remove a &mut by destructuring and then take a mutable reference to the value again. It's more obvious when we don't use the Option:
let mr: &mut Vec<u32> = &mut vec![];
{
let &mut ref mut a = mr;
a.push(3);
}
mr.push(4);
This also works. The third (special) line is equivalent to:
let a = &mut *mr ;
// ^^^----- this is an lvalue of type `Vec<u32>`
// ^^^^^^^^^^^^----- together it's of type `&mut Vec<u32>` again
In the Option case, we can't use the &mut *X version, but need to do all of it inside of the pattern. Thus the &mut ref mut x.

What is the right way to share a reference between closures if the value outlives the closures?

I want to share a reference between two closures; mutably in one closure. This is an artificial situation, but I find it interesting in the context of learning Rust.
In order to make it work, I had to make use of Rc, Weak, and RefCell. Is there a simpler way of achieving this?
use std::cell::RefCell;
use std::rc::Rc;
#[derive(Debug)]
struct Foo {
i: i32,
}
impl Foo {
fn get(&self) -> i32 {
self.i
}
fn incr(&mut self) {
self.i += 1
}
}
fn retry<O, N>(mut operation: O, mut notify: N) -> i32
where
O: FnMut() -> i32,
N: FnMut() -> (),
{
operation();
notify();
operation()
}
fn something(f: &mut Foo) {
let f_rc = Rc::new(RefCell::new(f));
let f_weak = Rc::downgrade(&f_rc);
let operation = || {
// f.get()
let cell = f_weak.upgrade().unwrap();
let f = cell.borrow();
f.get()
};
let notify = || {
// f.incr();
let cell = f_weak.upgrade().unwrap();
let mut f = cell.borrow_mut();
f.incr();
};
retry(operation, notify);
println!("{:?}", f_rc);
}
fn main() {
let mut f = Foo { i: 1 };
something(&mut f);
}
Reference counting is unnecessary here because the entity lives longer than any of the closures. You can get away with:
fn something(f: &mut Foo) {
let f = RefCell::new(f);
let operation = || f.borrow().get();
let notify = || {
f.borrow_mut().incr();
};
retry(operation, notify);
println!("{:?}", f);
}
which is quite simpler.
The use of RefCell, however, is necessary to move the enforcement of the Aliasing XOR Mutability from compile-time to run-time.

Resources