Does Rust have bindings for tee(2) in std::io or otherwise? And if there are no bindings, how would I get that functionality in a Rust program?
The tee method existed in the standard library, but it was deprecated in 1.6.
You can use the tee crate to get the same functionality:
extern crate tee;
use tee::TeeReader;
use std::io::Read;
fn main() {
let mut reader = "It's over 9000!".as_bytes();
let mut teeout = Vec::new();
let mut stdout = Vec::new();
{
let mut tee = TeeReader::new(&mut reader, &mut teeout);
let _ = tee.read_to_end(&mut stdout);
}
println!("tee out -> {:?}", teeout);
println!("std out -> {:?}", stdout);
}
(example from the repo)
Related
I want to build a function that takes a HashMap reference as an argument. This HashMap should be shared between threads for read only access. The code example is very simple:
I insert some value into the HashMap, pass it to the function and want antoher thread to read that value. I get an Error that the borrowed value does not live long enough at line let exit_code = test(&m);. Why is this not working?
use std::thread;
use std::collections::HashMap;
use std::sync::{Arc, RwLock };
fn main(){
let mut m: HashMap<u32, f64> = HashMap::new();
m.insert(0, 0.1);
let exit_code = test(&m);
std::process::exit(exit_code);
}
fn test(m: &'static HashMap<u32, f64>) -> i32{
let map_lock = Arc::new(RwLock::new(m));
let read_thread = thread::spawn(move || {
if let Ok(r_guard) = map_lock.read(){
println!("{:?}", r_guard.get(&0).unwrap());
}
});
read_thread.join().unwrap();
return 0;
}
if I don't put the 'static in the function signature for the HashMap argument, Arc::new(RwLock::new(m)); doesn't work. How can I sovlve this problem?
A reference is not safe to share unless is 'static meaning that something will live for the extent of the program. Otherwise the compiler is not able to track the liveliness of the shared element.
You should wrap it outside of the function, and take ownership of an Arc:
use std::thread;
use std::collections::HashMap;
use std::sync::{Arc, RwLock };
fn main(){
let mut map = HashMap::new();
map.insert(0, 0.1);
let m = Arc::new(RwLock::new(map));
let exit_code = test(m);
std::process::exit(exit_code);
}
fn test(map_lock: Arc<RwLock<HashMap<u32, f64>>>) -> i32 {
let read_thread = thread::spawn(move || {
if let Ok(r_guard) = map_lock.read(){
println!("{:?}", r_guard.get(&0).unwrap());
}
});
read_thread.join().unwrap();
return 0;
}
Playground
I am trying to use DHT11 Library for my STM32F303VC
I am getting error:
error[E0277]: the trait bound `PE2<Output<OpenDrain>>: _embedded_hal_digital_InputPin` is not satisfied
--> src/DHT11/auxiliary/src/lib.rs:51:32
|
51 | let mut dht11 = Dht11::new(pin);
| ^^^ the trait `_embedded_hal_digital_InputPin` is not implemented for `PE2<Output<OpenDrain>>`
|
= note: required because of the requirements on the impl of `embedded_hal::digital::v2::InputPin` for `PE2<Output<OpenDrain>>`
= note: required by `Dht11::<GPIO>::new`
My Error Image:
My code is in auxilary module is:
//! Initialization code
#![no_std]
#[allow(unused_extern_crates)] // bug rust-lang/rust#53964
extern crate panic_itm; // panic handler
pub use cortex_m::{asm::bkpt, iprint, iprintln};
pub use cortex_m_rt::entry;
pub use f3::hal::{delay::Delay, prelude, stm32f30x::i2c1};
pub use f3::led::{Direction, Leds};
pub use m::Float as _0;
pub use f3::hal::stm32f30x::{gpioc, rcc};
pub use dht11::{self,Measurement,Dht11};
pub use stm32f30x_hal::gpio;
use f3::hal::stm32f30x::{self, GPIOE, RCC};
pub use embedded_hal::digital::v2::OutputPin;
pub use embedded_hal::digital::v2::InputPin;
use cortex_m::peripheral::ITM;
use f3::{
hal::{
i2c::I2c,
prelude::*,
},
Lsm303dlhc,
};
pub fn init() -> (Delay, ITM, Leds, Dht11<GPIOE>) {
(stm32f30x::Peripherals::take().unwrap());
let cp = cortex_m::Peripherals::take().unwrap();
let dp = stm32f30x::Peripherals::take().unwrap();
let mut flash = dp.FLASH.constrain();
let mut rcc = dp.RCC.constrain();
let clocks = rcc.cfgr.freeze(&mut flash.acr);
let gpioe = dp.GPIOE.split(&mut rcc.ahb);
let leds = Leds::new(gpioe);
let mut gpiob = dp.GPIOB.split(&mut rcc.ahb);
let scl = gpiob.pb6.into_af4(&mut gpiob.moder, &mut gpiob.afrl);
let sda = gpiob.pb7.into_af4(&mut gpiob.moder, &mut gpiob.afrl);
let i2c = I2c::i2c1(dp.I2C1, (scl, sda), 400.khz(), clocks, &mut rcc.apb1);
let pin = gpioe.pe2.into_open_drain_output(&mut gpioe.moder,&mut gpioe.otyper);
let delay = Delay::new(cp.SYST, clocks);
let mut dht11 = Dht11::new(pin);
(delay, cp.ITM, leds, dht11)
}
my main.rs code is:
#![deny(unsafe_code)]
#![no_main]
#![no_std]
#[allow(unused_imports)]
use aux19::{entry, iprint, iprintln, prelude::*, Direction};
use aux19::{prelude::_embedded_hal_blocking_delay_DelayMs};
use m::Float;
// Slave address
const MAGNETOMETER: u8 = 0b001_1110;
#[entry]
fn main() -> ! {
let (mut delay, mut itm,mut leds,mut dth11) = aux18::init();
loop {
match dht11.perform_measurement(&mut delay) {
Ok(meas) => iprintln!(&mut itm.stim[0],"Temp: {} Hum: {}", meas.temperature, meas.humidity).unwrap(),
Err(e) => iprintln!(&mut itm.stim[0],"Error: {:?}", e).unwrap(),
};
delay.delay_ms(2_000_u16);
}
}
Dht11::new expects the pin to be an input pin, i.e. a type that implements embedded_hal::digital::v2::InputPin. In your auxiliary module, you configure the pin to be an output pin, which is the opposite of what you need to do:
let pin = gpioe.pe2.into_open_drain_output(&mut gpioe.moder,&mut gpioe.otyper);
The HAL library you are using has several methods to put a pin into input mode. into_floating_input might work for your use case. If you need a pull-up or pull-down resistor, there's also into_pull_down_input and into_pull_up_input. See reference documentation (for some reason, you need to expand the implementation blocks by clicking "+" to see the methods; this also prevents me from linking to them directly).
Using one of those should resolve this error.
I'm trying to figure out build a feature which requires reading the contents of a file into a futures::stream::BoxStream but I'm having a tough time figuring out what I need to do.
I have figured out how to read a file byte by byte via Bytes which implements an iterator.
use std::fs::File;
use std::io::prelude::*;
use std::io::{BufReader, Bytes};
// TODO: Convert this to a async Stream
fn async_read() -> Box<dyn Iterator<Item = Result<u8, std::io::Error>>> {
let f = File::open("/dev/random").expect("Could not open file");
let reader = BufReader::new(f);
let iter = reader.bytes().into_iter();
Box::new(iter)
}
fn main() {
ctrlc::set_handler(move || {
println!("received Ctrl+C!");
std::process::exit(0);
})
.expect("Error setting Ctrl-C handler");
for b in async_read().into_iter() {
println!("{:?}", b);
}
}
However, I've been struggling a bunch trying to figure out how I can turn this Box<dyn Iterator<Item = Result<u8, std::io::Error>>> into an Stream.
I would have thought something like this would work:
use futures::stream;
use std::fs::File;
use std::io::prelude::*;
use std::io::{BufReader, Bytes};
// TODO: Convert this to a async Stream
fn async_read() -> stream::BoxStream<'static, dyn Iterator<Item = Result<u8, std::io::Error>>> {
let f = File::open("/dev/random").expect("Could not open file");
let reader = BufReader::new(f);
let iter = reader.bytes().into_iter();
std::pin::Pin::new(Box::new(stream::iter(iter)))
}
fn main() {
ctrlc::set_handler(move || {
println!("received Ctrl+C!");
std::process::exit(0);
})
.expect("Error setting Ctrl-C handler");
while let Some(b) = async_read().poll() {
println!("{:?}", b);
}
}
But I keep getting a ton of compiler errors, I've tried other permutations but generally getting no where.
One of the compiler errors:
std::pin::Pin::new
``` --> src/main.rs:14:24
|
14 | std::pin::Pin::new(Box::new(stream::iter(iter)))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn std::iter::Iterator`, found enum `std::result::Result`
Anyone have any advice?
I'm pretty new to Rust, and specifically Streams/lower level stuff so I apologize if I got anything wrong, feel free to correct me.
For some additional background, I'm trying to do this so you can CTRL-C out of a command in nushell
I think you are overcomplicating it a bit, you can just return impl Stream from async_read, there is no need to box or pin (same goes for the original Iterator-based version). Then you need to set up an async runtime in order to poll the stream (in this example I just use the runtime provided by futures::executor::block_on). Then you can call futures::stream::StreamExt::next() on the stream to get a future representing the next item.
Here is one way to do this:
use futures::prelude::*;
use std::{
fs::File,
io::{prelude::*, BufReader},
};
fn async_read() -> impl Stream<Item = Result<u8, std::io::Error>> {
let f = File::open("/dev/random").expect("Could not open file");
let reader = BufReader::new(f);
stream::iter(reader.bytes())
}
async fn async_main() {
while let Some(b) = async_read().next().await {
println!("{:?}", b);
}
}
fn main() {
ctrlc::set_handler(move || {
println!("received Ctrl+C!");
std::process::exit(0);
})
.expect("Error setting Ctrl-C handler");
futures::executor::block_on(async_main());
}
I need to download a 60MB ZIP file and extract the only file that comes within it. I want to download it and extract it using streams. How can I achieve this using Rust?
fn main () {
let mut res = reqwest::get("myfile.zip").unwrap();
// extract the response body to myfile.txt
}
In Node.js I would do something like this:
http.get('myfile.zip', response => {
response.pipe(unzip.Parse())
.on('entry', entry => {
if (entry.path.endsWith('.txt')) {
entry.pipe(fs.createWriteStream('myfile.txt'))
}
})
})
With reqwest you can get the .zip file:
reqwest::get("myfile.zip")
Since reqwest can only be used for retrieving the file, ZipArchive from the zip crate can be used for unpacking it. It's not possible to stream the .zip file into ZipArchive, since ZipArchive::new(reader: R) requires R to implement Read (which is fulfilled by the Response of reqwest) and Seek, which is not implemented by Response.
As a workaround you may use a temporary file:
copy_to(&mut tmpfile)
As File implements both Seek and Read, zip can be used here:
zip::ZipArchive::new(tmpfile)
This is a working example of the described method:
extern crate reqwest;
extern crate tempfile;
extern crate zip;
use std::io::Read;
fn main() {
let mut tmpfile = tempfile::tempfile().unwrap();
reqwest::get("myfile.zip").unwrap().copy_to(&mut tmpfile);
let mut zip = zip::ZipArchive::new(tmpfile).unwrap();
println!("{:#?}", zip);
}
tempfile is a handy crate, which lets you create a temporary file, so you don't have to think of a name.
That's how I'd read the file hello.txt with content hello world from the archive hello.zip located on a local server:
extern crate reqwest;
extern crate zip;
use std::io::Read;
fn main() {
let mut res = reqwest::get("http://localhost:8000/hello.zip").unwrap();
let mut buf: Vec<u8> = Vec::new();
let _ = res.read_to_end(&mut buf);
let reader = std::io::Cursor::new(buf);
let mut zip = zip::ZipArchive::new(reader).unwrap();
let mut file_zip = zip.by_name("hello.txt").unwrap();
let mut file_buf: Vec<u8> = Vec::new();
let _ = file_zip.read_to_end(&mut file_buf);
let content = String::from_utf8(file_buf).unwrap();
println!("{}", content);
}
This will output hello world
async solution using Tokio
It's a bit convoluted, but you can do this using tokio, futures, tokio_util::compat and async_compression. The key is to create a futures::io::AsyncRead stream using .into_async_read() and then convert it into a tokio::io::AsyncRead using .compat().
For simplicity, it downloads a txt.gz file and prints it line by line.
use async_compression::tokio::bufread::GzipDecoder;
use futures::stream::TryStreamExt;
use tokio::io::AsyncBufReadExt;
use tokio_util::compat::FuturesAsyncReadCompatExt;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let url = "https://f001.backblazeb2.com/file/korteur/hello-world.txt.gz";
let response = reqwest::get(url).await?;
let stream = response
.bytes_stream()
.map_err(|e| futures::io::Error::new(futures::io::ErrorKind::Other, e))
.into_async_read()
.compat();
let gzip_decoder = GzipDecoder::new(stream);
// Print decompressed txt content
let buf_reader = tokio::io::BufReader::new(gzip_decoder);
let mut lines = buf_reader.lines();
while let Some(line) = lines.next_line().await? {
println!("{line}");
}
Ok(())
}
Credit to Benjamin Kay.
I'm trying to write a TCP client to print incoming messages. I came up with the following code:
extern crate bytes;
extern crate futures;
extern crate tokio_core;
extern crate tokio_io;
use futures::Future;
use tokio_core::net::TcpStream;
use tokio_core::reactor::Core;
use tokio_io::AsyncRead;
use bytes::BytesMut;
fn main() {
let mut core = Core::new().unwrap();
let handle = core.handle();
let connection = TcpStream::connect(&"127.0.0.1:8081".parse().unwrap(), &handle);
let server = connection.and_then(move |mut stream| {
let mut buf = BytesMut::with_capacity(1000);
stream
.read_buf(&mut buf)
.map(|buf| print!("Buffer {:?}", buf))
.map_err(|e| eprintln!("Error: {}", e));
Ok(())
});
core.run(server).unwrap();
}
It compiles but it fails with a Buffer NotReady error.
Rust is a compiled language, which means that you should pay attention to the warnings that the compiler generates:
warning: unused `std::result::Result` which must be used
--> src/main.rs:20:9
|
20 | / stream
21 | | .read_buf(&mut buf)
22 | | .map(|buf| print!("Buffer {:?}", buf))
23 | | .map_err(|e| eprintln!("Error: {}", e));
| |____________________________________________________^
|
= note: #[warn(unused_must_use)] on by default
Additionally, tokio has an entire chapter dedicated to low-level IO which I'll assume you've read to not bore you with details you already know.
First we take the connection Future and convert it into a Stream. A stream can yield multiple values — in this case we return one value for every successful read. We create AsWeGetIt for the simplest implementation of this.
We then print out each value of the stream using Stream::for_each. Conveniently, this performs the corresponding conversion back to a Future, which is what is needed for and_then.
extern crate bytes;
extern crate futures;
extern crate tokio_core;
extern crate tokio_io;
use futures::{Future, Poll, Stream};
use tokio_core::net::TcpStream;
use tokio_core::reactor::Core;
use tokio_io::AsyncRead;
use bytes::BytesMut;
struct AsWeGetIt<R>(R);
impl<R> Stream for AsWeGetIt<R>
where
R: AsyncRead,
{
type Item = BytesMut;
type Error = std::io::Error;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
let mut buf = BytesMut::with_capacity(1000);
self.0
.read_buf(&mut buf)
.map(|async| async.map(|_| Some(buf)))
}
}
fn main() {
let mut core = Core::new().unwrap();
let handle = core.handle();
let address = "127.0.0.1:8081".parse().expect("Unable to parse address");
let connection = TcpStream::connect(&address, &handle);
let client = connection
.and_then(|tcp_stream| {
AsWeGetIt(tcp_stream).for_each(|buf| {
println!("Buffer {:?}", buf);
Ok(())
})
})
.map_err(|e| eprintln!("Error: {}", e));
core.run(client).expect("Unable to run the event loop");
}