I am learning Rust by just coding right after the first 4 chapters of The Book. Getting started I am still getting used to how borrowing and sharing work and how we can take advantage of them in code.
This snippet of code is supposed to prompt user for an IP address, and if enter is pressed, then to return the loopback address. It works fine, but I am curious to know how this could be improved in any ways, because I definitely know it can. Thank you!
fn prompt_host() -> String {
let mut input_text = String::new();
println!(" input host IP, press enter for loopback:");
io::stdin()
.read_line(&mut input_text)
.expect(" ERROR: failed to read from stdin");
let len = input_text.len();
input_text.truncate(len - 1);
if input_text == "" {
return String::from("127.0.0.1");
}
return input_text as String;
}
Just some hints:
I think I would return something related to std::net::IpAddr instead of String (if there's a type for your needs, I would use it).
std::net::IpAddr implements FromStr, so you can use input_text.parse() and obtain a Result<IpAddr, Err> (since the conversion from string may fail).
I would use trim to get rid of spaces.
I would use is_empty to test if the string is empty. - Or even cover this case by just using parse.
There are (at least) two places that may fail: read_line and parse, so I would think about returning an Option<IpAddr> or even Result<IpAddr, ErrorType> for an appropriate ErrorType.
Related
I want to write an application in Rust that deals with input from terminal and I want to prevent it from crashing/being killed by running out of memory. It displays a prompt, processes a command and displays prompt again.
Basically I am looking for read_line_max(n) or read_until(delimiter, max_chars) API where at most n bytes or until delimiter is reached are read.
Possibilities I considered:
for io::stdin().lock().take(n).lines() takes n bytes total at most, I want unlimited chars but limit line size.
for io::stdin().lock().lines().take(n) limits number of lines
for line in io::stdin().lock().lines() {
let line = line?.chars().take(n);
println!("{}", respond_to(line));
}
too late, hogging 50GB of memory, kill imminent
I found out that BufReader used to have .chars() iterator that could be used in this way but it was removed.
With io::stdin().lock().read(buff) there is an issue with bytes vs chars but it may be my best bet. And then try to throw it into a String to check UTF-8 validity but that seems like something I would do in C and very unidiomatic.
Actually while writing this I quickly put together this thing:
let inp = io::stdin();
let mut bufinp = inp.lock();
let mut linebytes = [0_u8; 10];
loop {
match bufinp.read(&mut linebytes) {
Ok(bytes_read) => {
match String::from_utf8(linebytes[..bytes_read].to_vec()) {
Ok(line) => println!("processed line: {}", &line),
Err(err) => eprintln!("utf8 err: {:?}", err),
}
},
Err(err) => {
eprintln!("line read err: {:?}", err);
}
}
}
So... that kind of does what I want but I have some issues with it:
1) I need to trim the '\n' if input is smaller than buffer.
2) It doesn't clear rest of the stdin if input is larger than buffer. I'm guessing I need to put a skip_while() there at the end so that it doesn't spill over to the next read. Is there a nicer way to clear it?
3) It may split graphemes while I could in in fact handle those additional 3 bytes. I don't really care about reading up until specific hard limit. I just want to prevent the input/memory usage from being "too much".
4) It's just too low-level and complicated and not in line with "make good and safe choices easy to code and unsafe ones less available" which makes me think I'm not doing it right. But at least cat /dev/zero | ./target/debug/test doesn't result in SIGQUIT anymore.
I find it strange that a language that prides itself on safety wouldn't provide a fool-proof way to deal with potentially large input. Am I missing something or thinking too much about it? Every article I found just closes its eyes and fires read_to_end() or read_line() without much thought.
How should I read user input safely and idiomatically?
The documentation tells me that the swap_with_slice() method can potentially panic if the slices are of two different lengths. I am writing a parser which deals with data coming across TCP. My encoding scheme is such that the following should never panic, unless it is a malformed request. In this case, I don't want my programme to crash, but instead handle the error. How do I capture whether swap_with_slice panics or not? Since it itself does not return a Result?
let mut bytes_id = [0; 16];
bytes_id.swap_with_slice(&mut self.raw_bytes[Headers::MessageLength as usize..Headers::Data as usize]);
This should work
let result = std::panic::catch_unwind(|| <expected_to_panic_operation_here>);
This question already has answers here:
How to read a string from keyboard on the same line with output? [duplicate]
(2 answers)
Closed 7 years ago.
I'm totally new to Rust, and in fact just started today. (For those who saw my previous deleted question, you have my most sincere apology.)
I'm just trying to build a simple application to sharpen my skills, but I meet a problem. I like to create something where you can just type stuff, and for those who know dunnet, start each line with two larger than signs.
But I haven't really found out how to read the bits after the two signs. I searched on Google and StackOverflow, but this is the best I got:
//Starting a line
fn start(){print!(">>");}
//Seeking input and saving input
fn line_init() -> String
{
use std::io;
use std::io::BufRead;
start();
let mut input = String::new();
let stdin = io::stdin();
stdin.lock().read_line(&mut input).unwrap();
input
}
//Main function
fn main() {
loop
{
print!("{}", line_init());
}
}
But of course, the user input line lands on a new line, which isn't exactly what I want. This is the C++ version of the thing I want (in fact, I'm just duplicating my C++ project in Rust):
cout << ">>";
string input;
getline(cin, input);
Sorry that this post is getting a bit long!
Do you mean that the user sees something like the following on the console?
hello
>>
instead of
>> hello
?
In that case, the problem is that Rust stdout is line-buffered by default, so unless you write a newline, nothing will be printed to the console.
You need to flush the stdout after you write the line prefix:
fn start(){
print!(">>");
io::stdout().flush().unwrap();
}
I've seen a similar topic on sof but its solution did not help me. This is ticking my mind and basically all i want is to have some method of accessing and modifying a value that will maintain its last changed state through out my macros in my single .ahk file.
See example below ,
~Home::Suspend
XButton1::
tog()
return
LButton::
shot()
return
var := "1"
tog(){
var *= -1
}
shot(){
If (var = "1") {
Loop, 1 {
Send {k}
Sleep 65
Send {WheelDown}
Sleep 100
Send {WheelUP}
Sleep 10
}
} Else {
Send {k}
}
}
I am aware that the above is incorrect, and i tried to use"global" in my functions but i just couldn't get my desired effect.
Using the "global" should work. Something like:
shot(){
global var
If (var = "1") {
That points the 'var' variable in the shot() function to the existing 'var' variable defined outside the function.
I had the same issue and after some trial and error I found my mistake which is the same as in the provided code:
The correct way to declare a global is before other functions
var := "1"
XButton1::
; code...
return
The code in the OPs script will hit the return first and never declare the variable
XButton1::
; code...
return ; Returns Here
var := "1" ; Incorrect Will Not Be Declared
I just wanted to provide this as an answer because while I did see this information in one of the comments, I didn't see it until after I'd already spent an additional hour figuring it out myself. As this is the answer I needed, having it as an actual prominent answer may help someone else save time.
What I did, especially since I sometimes have multiple scripts running that need to access the same variable, is to place the var in a .ini file. I also use this to preserve the variable value after a restart. The solution is somewhat slower since the data is saved to the hard disk / SSD, but it works beautifully.
Example of writing the value "S" to variable "State" in group "Finish"
IniWrite, S, C:\2Podcasts\FinishOptions.ini, Finish, State
In an other script (other AutoHotKey instance), I read the value and assign it to the variable "FinishOption".
IniRead, FinishOption, C:\2Podcasts\FinishOptions.ini, Finish, State
If you want to toggle values (True/False), you could use this. This will do an IF on the current value of the variable AND set the variable to the opposite value.
If (MyLoop := !MyLoop) ; Toggle the variable "MyLoop" True/False
{
Do something
}
Else
{
Do something else
}
return
Okay, so apparently this works:
void foo(size_t s) {
int myArray[s];
// ... use myArray...
}
Is this really legal? I mean, it must be, because it compiles (where the C compiler would reject it as non-constant). The first part of my question is: how does this work? I assume it's allocating it on the stack? Is this different from using alloca()?
Practically, I found some code that does this:
void bar(size_t chunkSize) {
CFReadStreamRef foo = NULL;
// ...some stuff to init foo...
while (stuffToDo) {
UInt8 buffer[chunkSize];
// ...read some data from stream into buffer
// using CFReadStreamRead()...
}
}
This works. However, when I move the buffer allocation from inside the loop to the first line of the function (directly before foo is declared), the function... stops working. In the debugger it gets to the first access of local variables and then just... exits. I don't see any exceptions being thrown, it doesn't crash, it just program carries on running (in reality the function returns a string and that return value is NULL, which is what the return variable is initialized to). I'm not sure what's going on. The second part of my questions is, in light of the first part, what the heck is going on?
it is legal in C99, although dangerous, and yes -- it is like alloca.
because it's like alloca, you want reasonably sized arrays when allocating on the stack. i am not sure if this is defined if the length is zero, but you could definitely cause a stack overflow if the array is 'large enough' to do so.
as far as what is going on -- pulling it out of the loop should make no difference if the sizes are reasonable. i suspect you are seeing undefined behavior because a parameter value is too large (or perhaps 0) -- you should validate the chunkSize parameter. the assembly will tell you why pulling it out of the loop makes a difference (assuming everything else in the program is well-formed).