I am very new to Rust and to low-level languages in general. I noticed that in the code below, if I replace loop with while true the code doesn't compile anymore. I would actually expect the version with loop not to compile as well, because the return statement is inside an if statement, so not all code paths would return a value.
pub fn two_sum(numbers: Vec<i32>, target: i32) -> Vec<i32> {
let mut left = 0;
let mut right = numbers.len() - 1;
loop {
while numbers[left] + numbers[right] > target {
right -= 1;
}
if numbers[left] + numbers[right] == target {
break vec![left as i32 + 1, right as i32 + 1];
} else {
left += 1;
}
}
}
I am also open to suggestions on how to make the code above more Rust idiomatic!
That is because loop's control flow is much better understood by the compiler:
the compiler understands that a loop is executed at least once
a loop without a break has type ! (never), which unifies with all types (in type-system lingo it's a "bottom" type), a loop with a break has type (), a loop with a valued break has whatever type break's value has
this means the compiler can reason about loop termination (or the lack thereof), if the loop does not contain a break statement it knows that the code following the loop is dead, it can never execute
None of that is the case of a while loop, there is no special-case for while true, or while false, they're not treated any differently than any other while loop: as far as the compiler is concerned they can all run for 0+ iterations
Hence:
let mut a;
loop {
a = 1;
break;
}
a
compiles but
let mut a;
while true {
a = 1;
break;
}
a
does not compile. That is also why loop can have a result value but while can not.
Related
I'm trying to implement a commonly used pattern - using the result of a previous loop iteration in the next loop iteration. For example, to implement pagination where you need to give the id of the last value on the previous page.
struct Result {
str: String,
}
fn main() {
let times = 10;
let mut last: Option<&str> = None;
for i in 0..times {
let current = do_something(last);
last = match current {
Some(r) => Some(&r.str.to_owned()),
None => None,
};
}
}
fn do_something(o: Option<&str>) -> Option<Result> {
Some(Result {
str: "whatever string".to_string(),
})
}
However, I'm not sure how to actually get the value out of the loop. Currently, the compiler error is temporary value dropped while borrowed (at &r.str.to_owned()), though I made many other attempts, but to no avail.
The only way I found to actually get it working is to create some sort of local tmp_str variable and do a hack like this:
match current {
Some(r) => {
tmp_str.clone_from(&r.str);
last = Some(&tmp_str);
}
None => {
last = None;
}
}
But that doesn't feel like it's the way it's supposed to be done.
In your code, it remains unclear who the owner of the String referenced in last: Option<&str> is supposed to be. You could introduce an extra mutable local variable that owns the string. But then you would have two variables: the owner and the reference, which seems redundant. It would be much simpler to just make last the owner:
struct MyRes {
str: String,
}
fn main() {
let times = 10;
let mut last: Option<String> = None;
for _i in 0..times {
last = do_something(&last).map(|r| r.str);
}
}
fn do_something(_o: &Option<String>) -> Option<MyRes> {
Some(MyRes {
str: "whatever string".to_string(),
})
}
In do_something, you can just pass the whole argument by reference, this seems more likely to be what you wanted. Also note that naming your own struct Result is a bad idea, because Result is such a pervasive trait built deeply into the compiler (?-operator etc).
Follow-up question: Option<&str> or Option<String>?
Both Option<&str> and Option<String> have different trade-offs. One is better for passing string literals, other is better for passing owned Strings. I'd actually propose to use neither, and instead make the function generic over type S that implements AsRef<str>. Here is a comparison of various methods:
fn do_something(o: &Option<String>) {
let _a: Option<&str> = o.as_ref().map(|r| &**r);
let _b: Option<String> = o.clone();
}
fn do_something2(o: &Option<&str>) {
let _a: Option<&str> = o.clone(); // do you need it?
let _b: Option<String> = o.map(|r| r.to_string());
}
fn do_something3<S: AsRef<str>>(o: &Option<S>) {
let _a: Option<&str> = o.as_ref().map(|s| s.as_ref());
let _b: Option<String> = o.as_ref().map(|r| r.as_ref().to_string());
}
fn main() {
let x: Option<String> = None;
let y: Option<&str> = None;
do_something(&x); // nice
do_something(&y.map(|r| r.to_string())); // awkward & expensive
do_something2(&x.as_ref().map(|x| &**x)); // cheap but awkward
do_something2(&y); // nice
do_something3(&x); // nice
do_something3(&y); // nice, in both cases
}
Note that not all of the above combinations are very idiomatic, some are added just for completeness (e.g. asking for AsRef<str> and then building an owned String out of seems a bit strange).
r.str.to_owned() is a temporary value. You can take a reference to a temporary, but because the temporary value will usually be dropped (destroyed) at the end of the innermost enclosing statement, the reference becomes dangling at that point. In this case the "innermost enclosing statement" is either the last line of the loop, or the loop body itself -- I'm not sure exactly which one applies here, but it doesn't matter, because either way, you're trying to make last contain a reference to a String that will soon be dropped, making last unusable. The compiler is right to stop you from using it again in the next iteration of the loop.
The easiest fix is just to not make last a reference at all -- in the example, it's not necessary or desirable. Just use Option<String>:
fn main() {
let times = 10;
let mut last = None;
for _ in 0..times {
last = match do_something(last) {
Some(r) => Some(r.str),
None => None,
};
}
}
fn do_something(_: Option<String>) -> Option<Result> {
// ...
}
There are also ways to make the reference version work; here is one:
let mut current; // lift this declaration out of the loop so `current` will have
// a lifetime longer than one iteration
for _ in 0..times {
current = do_something(last);
last = match current {
Some(ref r) => Some(&r.str), // borrow from `current` in the loop instead
// of from a newly created String
None => None,
};
}
You might want to do this if your code is more complicated than the example and using String would mean a lot of potentially expensive .clone()s.
I have this little guessing game code. In order to deal with the string input, I used try/catch blocks. Try works perfectly, but catch-block is outside of the loop and I can't seem to make it work inside. So the program stops after catching an exception. What should I do so that my loop continues after catching an exception?
fun main(args: Array<String>) {
val rand = java.util.Random()
val n = 1 + rand.nextInt(100)
var guess: Int
var numberOfTries = 0
println("I guessed a number from 1 до 100. What is it?\n")
try {
do {
guess = readLine()!!.toInt()
var x = Math.abs(n - guess)
numberOfTries++
when (x) {
in 1..3 -> println("А-а-аh! It's burning!")
in 4..7 -> println("Really hot!")
in 8..15 -> println("Warm")
in 16..31 -> println("A bit warm!")
in 32..63 -> println("Pretty cold")
in 64..99 -> println("It's freezing!")
}
} while (guess != n)
} catch (e: NumberFormatException) {
println("Use digits, please!") }
println("Wow! You only used $numberOfTries tries!")
}
As MFazio23 mentioned, you use the try inside the while loop. Otherwise, it will exit the loop if an exception is thrown.
If an exception is thrown, anything inside is halted, which includes further code. if you have a method that throws an exception, no code after it will be executed. The try-catch creates an entry-point for the exception; your code will continue inside the relevant catch block (or exit the program if there is none), which means the loop inside the try-catch will stop.
However, you actually don't need the try-catch at all. Kotlin has a nice extension function called toIntOrNull, which does exactly what you'd expect; it attempts to convert the input to an int, and returns the number, or null if it failed. So, you can do this:
fun main(args: Array<String>) {
val rand = java.util.Random()
val n = 1 + rand.nextInt(100)
var guess: Int?
var numberOfTries = 0
println("I guessed a number from 1 до 100. What is it?\n")
do {
guess = readLine()?.toIntOrNull() // Note that this now uses ?. instead of !!. This is to make the null check useful If it throws an NPE, it kinda defeats the point. If the line is null, it now prints the same message as an invalid number
// numberOfTries++ // move this up here if you want to count invalid guesses as a guess
if(guess == null){
System.out.println("Only use numbers")
continue;
}
val x = Math.abs(n - guess)// I also changed this to a val; it's immutable, so it doesn't need to be a var
numberOfTries++
when (x) {
in 1..3 -> println("А-а-аh! It's burning!")
in 4..7 -> println("Really hot!")
in 8..15 -> println("Warm")
in 16..31 -> println("A bit warm!")
in 32..63 -> println("Pretty cold")
in 64..99 -> println("It's freezing!")
}
} while (guess != n)
println("Wow! You only used $numberOfTries tries!")
}
You can also optimize it further, but using an extension function/variable (I'm not sure what it is, it's a variable declared as an extension function, but since there's a getter too, I'm not sure what to call it) called absoluteValue.
You could also use if-statements, but it is slightly more boilerplate than using this. You cannot call Math.abs with null, because it uses primitives. Primitives in Java can never be null.
Which means anything you pass you the method cannot be null, which in Kotlin means for an instance Int. If it's nullable, it's an Int?, but the method requires non-null from Kotlin. You can't pass Int? to Int (you can do it the other way around, but that's not relevant here).
Under the hood, .absoluteValue calls Math.abs(n), but since it's a call, you can use the null-safe operator (?.)
guess = readLine()?.toIntOrNull()
val x = guess?.absoluteValue
numberOfTries++
when (x) {
in 1..3 -> println("А-а-аh! It's burning!")
in 4..7 -> println("Really hot!")
in 8..15 -> println("Warm")
in 16..31 -> println("A bit warm!")
in 32..63 -> println("Pretty cold")
in 64..99 -> println("It's freezing!")
null -> println("Please only use numbers")
}
And now that x is nullable, you can add null to the when statement (in response to your comment).
Also, if you only want numberOfTries to increment on valid numbers, add an if(x != null) before you call it.
You should be able to add the try...catch block right in your do...while. The only other change needed would be to initialize guess with a value (since it's not guaranteed to be set before the while block is hit):
val rand = java.util.Random()
val n = 1 + rand.nextInt(100)
var guess = 0
var numberOfTries = 0
println("I guessed a number from 1 до 100. What is it?\n")
do {
try {
guess = readLine()!!.toInt()
val x = Math.abs(n - guess)
numberOfTries++
when (x) {
in 1..3 -> println("А-а-аh! It's burning!")
in 4..7 -> println("Really hot!")
in 8..15 -> println("Warm")
in 16..31 -> println("A bit warm!")
in 32..63 -> println("Pretty cold")
in 64..99 -> println("It's freezing!")
}
} catch (e: NumberFormatException) {
println("Use digits, please!")
}
} while (guess != n)
println("Wow! You only used $numberOfTries tries!")
I have fully coded the program from "The Rust Programming Language" book online, chapter 2. I have also developed it a tiny bit further than provided: by adding a simple question/response where the user gets to play again by inputting "y".
However, I am experiencing a slight bug in my program. When a user opts to run the game again, the text output "Please input a (number) guess" is repeated twice. Of course, this does not take away from the main functionality of the program (it still functions fine after the repeat), but it does seem weird and I'd prefer to remove the issue now rather than leaving it.
I have done some debugging, which has resulted in me concluding it is definitely happening in the [figure 3] area in the code. To see how I did so, go to the debugging area below.
Code
extern crate rand;
#[macro_use]
extern crate text_io;
use rand::Rng;
use std::io;
use std::cmp::Ordering;
//Main code
fn main() {
// Generate random number, create premise
println!("Guess the number!");
let mut breaking_choice = false;
while !breaking_choice {
let secret_number = rand::thread_rng().gen_range(1, 101);
// **Problem area**
loop {
println!("Please input a (number) guess");
println!("1"); // [**REFERENCE 1**]
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
println!("2"); // [**REFERENCE 2**]
let guess: u32 = match guess.trim().parse() { // [FIGURE 3]
Ok(num) => num, // [FIGURE 3]
Err(_) => continue, // [FIGURE 3]
};
// ...until correct
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small"),
Ordering::Greater => println!("Too big"),
Ordering::Equal => {
println!("Correct!");
break;
}
}
}
// **Possible problem area**
println!("Would you like to continue playing? Y or N");
let choice: String = read!();
if choice == "N" {
breaking_choice = true
} else if choice == "n" {
breaking_choice = true
} else if choice == "Y" {
continue;
} else if choice == "y" {
continue;
}
}
}
Debugging
The first thing I did was place some simple println! commands at the main processing areas of the questionable code: [Reference 1] and [Reference 2]. This allowed me to see whether any of the commands were the problem in between them to give me a slightly better angle as to what is happening. I also replaced the random number generator with a set number (let secret_number = 5;)
This is what happened in compilation:
and this is what happened in execution:
From these results, I believe that the error is occurring in [FIGURE 3] (main input verification) because that is where it seems to stop.
I have put println!("3"); just before //...until correct, which doesn't output in the first loop. Therefore, it is at [FIGURE 3] that the program is having the problem.
I guess pressing enter leads to CRLF (\r\n) (on Windows; on Linux it seems to work fine), i.e. two whitespace characters. read! will only read one of them, and the next read_line call will return an empty line (string with only \n (0x0a)).
How to do code something like this in groovy?
do {
x.doIt()
} while (!x.isFinished())
Because there is no do ... while syntax in groovy.
No 'do ... while()' syntax as yet.
Due to ambiguity, we've not yet added support for do .. while to Groovy
References:
groovy - dev > do while
Migration From Classic to JSR syntax
Groovy Documentation > Control Structures > Looping
Rosetta Code > Loops/Do-while Groovy
You can roll your own looping that's almost what you want.
Here's an example with loop { code } until { condition }
You can't have a corresponding loop { code } while { condition } because while is a keyword.
But you could call it something else.
Anyway here's some rough and ready code for loop until.
One gotcha is you need to use braces for the until condition to make it a closure.
There may well be other issues with it.
class Looper {
private Closure code
static Looper loop( Closure code ) {
new Looper(code:code)
}
void until( Closure test ) {
code()
while (!test()) {
code()
}
}
}
Usage:
import static Looper.*
int i = 0
loop {
println("Looping : " + i)
i += 1
} until { i == 5 }
So many answers and not a single one without a redundant call, a shame ;)
This is the closest it can get to purely language syntax based do-while in Groovy:
while ({
x.doIt()
!x.isFinished()
}()) continue
The last statement within curly braces (within closure) is evaluated as a loop exit condition.
Instead of continue keyword a semicolon can be used.
Additional nice thing about it, loop can be parametrized (kind of), like:
Closure<Boolean> somethingToDo = { foo ->
foo.doIt()
!foo.isFinished()
}
and then elsewhere:
while (somethingToDo(x)) continue
Formerly I've proposed this answer over here: How do I iterate over all bytes in an inputStream using Groovy, given that it lacks a do-while statement?
Depending on your use case, there are options like this: do .. while() in Groovy with inputStream?
Or you can do:
x.doIt()
while( !x.finished ) { x.doIt() }
Or
while( true ) {
x.doIt()
if( x.finished ) break
}
You can use a condition variable with the regular while loop:
def keepGoing = true
while( keepGoing ){
doSomething()
keepGoing = ... // evaluate the loop condition here
}
Update Groovy 2.6 has been abandoned to concentrate on 3.0.
From Groovy 2.6 on, do-while is supported when enabling the new Parrot Parser, from Groovy 3.0 on this is the default. See release notes:
// classic Java-style do..while loop
def count = 5
def fact = 1
do {
fact *= count--
} while(count > 1)
assert fact == 120
By now, Groovy has support for do/while:
do {
x.doIt()
} while (!x.isFinished())
Or you can implement it in a Groovier way :
def loop(Closure g){
def valueHolder = [:]
g.delegate = valueHolder
g.resolveStrategy = Closure.DELEGATE_FIRST
g()
[until:{Closure w ->
w.delegate = valueHolder
w.resolveStrategy = Closure.DELEGATE_FIRST
while(!w()){
g()
}
}]
}
I'm trying to make a Pascal interpreter using ANTLR and currently have some troubles with processing loops while walking the AST tree.
For example for loop is parsed as:
parametricLoop
: FOR IDENTIFIER ASSIGN start = integerExpression TO end = integerExpression DO
statement
-> ^( PARAMETRIC_LOOP IDENTIFIER $start $end statement )
;
(variant with DOWNTO is ignored).
In what way can I make walker to repeat the loop's execution so much times as needed? I know that I should use input.Mark() and input.Rewind() for that. But exactly where should they be put? My current wrong variant looks so (target language is C#):
parametricLoop
:
^(
PARAMETRIC_LOOP
IDENTIFIER
start = integerExpression
{
Variable parameter = Members.variable($IDENTIFIER.text);
parameter.value = $start.result;
}
end = integerExpression
{
int end_value = $end.result;
if ((int)parameter.value > end_value) goto EndLoop;
parametric_loop_start = input.Mark();
}
statement
{
parameter.value = (int)parameter.value + 1;
if ((int)parameter.value <= end_value)
input.Rewind(parametric_loop_start);
)
{
EndLoop: ;
}
;
(Hope everything is understandable). The condition of repeating should be checked before the statement's first execution.
I tried to play with placing Mark and Rewind in different code blocks including #init and #after, and even put trailing goto to loops head, but each time loop either iterated one time or threw exceptions like Unexpected token met, for example ':=' (assignement). I have no idea, how to make that work properly and can't find any working example. Can anybody suggest a solution of this problem?
I haven't used ANTLR, but it seems to me that you are trying to execute the program while you're parsing it, but that's not really what parsers are designed for (simple arithmetic expressions can be executed during parsing, but as you have discovered, loops are problematic). I strongly suggest that you use the parsing only to construct the AST. So the parser code for parametricLoop should only construct a tree node that represents the loop, with child nodes representing the variables, conditions and body. Afterwards, in a separate, regular C# class (to which you provide the AST generated by the parser), you execute the code by traversing the tree in some manner, and then you have complete freedom to jump back and forth between the nodes in order to simulate the loop execution.
I work with ANTLR 3.4 and I found a solution which works with Class CommonTreeNodeStream.
Basically I splitted off new instances of my tree parser, which in turn analyzed all subtrees. My sample code defines a while-loop:
tree grammar Interpreter;
...
#members
{
...
private Interpreter (CommonTree node, Map<String, Integer> symbolTable)
{
this (new CommonTreeNodeStream (node));
...
}
...
}
...
stmt : ...
| ^(WHILE c=. s1=.) // ^(WHILE cond stmt)
{
for (;;)
{
Interpreter condition = new Interpreter (c, this.symbolTable);
boolean result = condition.cond ();
if (! result)
break;
Interpreter statement = new Interpreter (s1, this.symbolTable);
statement.stmt ();
}
}
...
cond returns [boolean result]
: ^(LT e1=expr e2=expr) {$result = ($e1.value < $e2.value);}
| ...
Just solved a similar problem, several points:
Seems you need to use BufferedTreeNodeStream instead of CommonTreeNodeStream, CommonTreeNodeStream never works for me (struggled long time to find out)
Use seek seems to be more clear to me
Here's my code for a list command, pretty sure yours can be easily changed to this style:
list returns [Object r]
: ^(LIST ID
{int e_index = input.Index;}
exp=.
{int s_index = input.Index;}
statements=.
)
{
int next = input.Index;
input.Seek(e_index);
object list = expression();
foreach(object o in (IEnumerable<object>)list)
{
model[$ID.Text] = o;
input.Seek(s_index);
$r += optional_block().ToString();
}
input.Seek(next);
}