英文:
Rust: Ownership problem when trying to get next byte from Stdin
问题
-
The reason for the error in your code is that the
bytes()
method takes ownership of the underlying reader, and you are trying to call it on a mutable reference to the reader (self.0
). This results in a move error because theR
type (the reader) does not implement theCopy
trait, and callingbytes()
consumes the reader. To fix this, you can use theby_ref()
method to create a reference that can be consumed bybytes()
without moving the original reader. Here's how you can modify your code to fix this issue:Replace this line:
let buf = self.0.by_ref().bytes()
with:
let buf = self.0.by_ref().bytes()
By using
by_ref()
, you create a reference to the reader, and thebytes()
method can work with it without taking ownership. -
To achieve a
getchar
-like functionality in Rust, your approach is reasonable. Thechar()
method you added to theIO
struct allows you to read a single character from stdin. However, the issue in your code needs to be fixed as explained in the previous answer. -
The difference you observed in the return type of
self.0.by_ref().bytes()
between the originalread
function and your code might be due to changes in the Rust standard library or how thebytes()
method is implemented in the specific version of Rust you are using. TheBytes
type returned bybytes()
represents a stream of bytes from the reader. In the original code, the reference types may have been handled differently or had different traits associated with them, resulting in the type difference. This is not unusual, and such differences can occur between different versions of libraries or code.
英文:
I was modifing a code snippet from github which enable fast i/o in Rust for competitive-programming.
I want to add a feature making it can read single character from stdin, just like getchar
in C. My approach was a combination of the pub fn read<T: std::str::FromStr>
in the original code snippet and this SO answer
//////////////////////////////////////////////////////////////////////
/// https://github.com/kenkoooo/competitive-programming-rs/blob/master/src/utils/scanner.rs
/// let (stdin, stdout) = (std::io::stdin(), std::io::stdout());
/// let mut sc = IO::new(stdin.lock(), stdout.lock());
pub struct IO<R, W: std::io::Write>(R, std::io::BufWriter<W>);
impl<R: std::io::Read, W: std::io::Write> IO<R, W> {
pub fn new(r: R, w: W) -> IO<R, W> {
IO(r, std::io::BufWriter::new(w))
}
pub fn write<S: ToString>(&mut self, s: S) {
use std::io::Write;
self.1.write_all(s.to_string().as_bytes()).unwrap();
}
pub fn read<T: std::str::FromStr>(&mut self) -> T {
use std::io::Read;
let buf = self
.0
.by_ref()
.bytes()
.map(|b| b.unwrap())
.skip_while(|&b| b == b' ' || b == b'\n' || b == b'\r' || b == b'\t')
.take_while(|&b| b != b' ' && b != b'\n' && b != b'\r' && b != b'\t')
.collect::<Vec<_>>();
unsafe { std::str::from_utf8_unchecked(&buf) }
.parse()
.ok()
.expect("Parse error.")
}
pub fn usize0(&mut self) -> usize {
self.read::<usize>() - 1
}
pub fn vec<T: std::str::FromStr>(&mut self, n: usize) -> Vec<T> {
(0..n).map(|_| self.read()).collect()
}
pub fn chars(&mut self) -> Vec<char> {
self.read::<String>().chars().collect()
}
pub fn char(&mut self) -> char {
self
.0
.by_ref()
.bytes()
.next()
.unwrap()
.unwrap() as char
}
}
///////////////////////////////////////////////////////////////////////
#[allow(non_snake_case)]
fn main() {
let (stdin, stdout) = (std::io::stdin(), std::io::stdout());
let mut sc = IO::new(stdin.lock(), stdout.lock());
let c = sc.char();
sc.write(c);
}
cargo run
and the output was:
$ cargo run
warning: unused manifest key: package.author
Compiling ralgo v0.1.0 (/home/xxx/ralgo)
error[E0507]: cannot move out of a mutable reference
--> src/main.rs:40:9
|
40 | / self
41 | | .0
42 | | .by_ref()
43 | | .bytes()
| | ------^
| |__________|_____|
| | move occurs because value has type `R`, which does not implement the `Copy` trait
| value moved due to this method call
|
note: `bytes` takes ownership of the receiver `self`, which moves value
--> /home/xxx/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/io/mod.rs:922:14
|
922 | fn bytes(self) -> Bytes<Self>
| ^^^^
For more information about this error, try `rustc --explain E0507`.
error: could not compile `ralgo` due to previous error
My questions:
- The reason for this error and how to fix it.
- Is there any better way to achieve
getchar
in Rust? - Why the rust-analyser shows the return type of
let buf = self.0.by_ref().bytes()
in the originalread
function isBytes<&mut R>
but for my codeself.0.by_ref().bytes()
it wasBytes<R>
?
答案1
得分: 2
Upfront, I want to note that your read
is unsound. And furthermore the cost of allocating a vector is almost certainly going to dwarf the cost of validating that said vector is UTF8, so it's rather unnecessary.
The reason for this error and how to fix it.
Because you don't use std::io::Read
globally, the compiler knows that R
specifically implements Read
(as that's a bound), it does not know about any other implementation of Read
. Notably, it does not know about impl<R: Read + ?Sized> Read for &mut R
. Thus as far as it is concerned, the closest thing which would have a bytes
method is the original reader, which it tries to deref. The error is much clearer if you assign the result of by_ref
to a local variable.
Just stop using fully qualified path like this, especially for traits. Your code is strictly less readable than if you just imported Read
, Write
, and BufWriter
at the top.
Is there any better way to achieve getchar in Rust?
getchar is a horrible function so I'm not too clear on why you'd want that. But I would suggest just using byteorder's read_u8
.
Why the rust-analyser shows the return type
Possibly because it's getting confused and assumes all the traits are in scope.
.skip_while(|&b| b == b' ' || b == b'\n' || b == b'\r' || b == b'\t')
.take_while(|&b| b != b' ' && b != b'\n' && b != b'\r' && b != b'\t')
These seem like complicated ways of not calling u8::is_ascii_whitespace
.
英文:
Upfront, I want to note that your read
is unsound. And furthermore the cost of allocating a vector is almost certainly going to dwarf the cost of validating that said vector is UTF8, so it's rather unnecessary.
> The reason for this error and how to fix it.
Because you don't use std::io::Read
globally, the compiler knows that R
specifically implements Read
(as that's a bound), it does not know about any other implementation of Read
. Notably, it does not know about impl<R: Read + ?Sized> Read for &mut R
. Thus as far as it is concerned, the closest thing which would have a bytes
method is the original reader, which it tries to deref'. The error is much clearer if you assign the result of by_ref
to a local variable.
Just stop using fully qualified path like this, especially for traits. Your code is strictly less readable than if you just imported Read
, Write
, and BufWriter
at the top.
> Is there any better way to achieve getchar in Rust?
getchar is a horrible function so I'm not too clear on why you'd want that. But I would suggest just using byteorder's read_u8
.
> Why the rust-analyser shows the return type
Possibly because it's getting confused and assumes all the traits are in scope.
> .skip_while(|&b| b == b' ' || b == b'\n' || b == b'\r' || b == b'\t')
> .take_while(|&b| b != b' ' && b != b'\n' && b != b'\r' && b != b'\t')
These seem like complicated ways of not calling u8::is_ascii_whitespace
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论