I have an application where I wish to create a new operator to work on one of my custom classes. The definition (sub infix:<α>) compiles without complaint, but when I attempt to use the operator, Raku reports two terms in a row.
I guess there is something weird I'm doing in my module file. Sometimes problems are cured by exiting and re-initializing interactive Raku - but it is perhaps so obvious I can't see it.
**UPDATE
**
Embedding these statements within the module works as expected - this leads me to suspect that I'm doing something wrong in the module, possibly related to exports (the "two terms in a row" message implies the compiler doesn't recognize the customer operators outside the module file).
There's nothing secret or magical about my module:
########################################################################
### Convert goofy old Perl5 module to Raku
########################################################################
unit module Format;
use List::Util;
=begin comment
Class supporting a bidrectional map lookup. Given a hash containing
keys and aliases (e.g., key=>alias) two lookaside hashes are created
to convert between the two values.
=end comment
class Bidirectional is export {
has Any %!aliasMap = ();
has Any %!keyMap = ();
has Any %.map = ();
has Bool $!initialized = False;
=begin comment
Do nothing if this object has already been initialized, otherwise loop
through the supplied map and populate the alias and key maps.
=end comment
method !initialize () {
if !($!initialized) {
for self.map.kv -> $key, $alias {
%!aliasMap{$alias} = $key;
%!keyMap{$key} = $alias;
}
$!initialized = True;
say %!aliasMap;
say %!keyMap;
}
return self;
}
=begin comment
Convert from an alias to a key value
=end comment
method aliasToKey (Str $alias) {
self!initialize();
return %!aliasMap{$alias} || $alias;
}
=begin comment
Convert from a key to an alias value.
=end comment
method keyToAlias (Str $key) {
self!initialize();
return %!keyMap{$key} || $key;
}
=begin comment
Return either a key from an alias (first priority) or an alias from a key
(second priority).
=end comment
method either (Str $candidate) {
return self.aliasToKey($candidate) || self.keyToAlias($candidate);
}
}
sub infix:<α> (Format::Bidirectional $name, Str $value) is export {
return $name.keyToAlias($value);
}
sub infix:<κ> (Format::Bidirectional $name, Str $value) is export {
return $name.aliasToKey($value);
}
sub infix:<∨> (Format::Bidirectional $name, Str $value) is export {
return $name.either($value);
}
class Formatter {
has Str $.span is rw;
has Str $.border is rw;
has Str $.top is rw;
has Str $.reading_order is rw;
has Bool $.locked is rw;
has Str $.left is rw;
has Str $.right_color is rw;
has Str $.font_outline is rw;
has Str $.rotation is rw;
has Str $.shrink is rw;
has Str $.text_wrap is rw;
has Str $.right is rw;
has Str $.bottom is rw;
has Str $.indent is rw;
has Str $.left_color is rw;
has Str $.font_encoding is rw;
has Str $.color is rw;
has Str $.font_script is rw;
has Str $.fg_color is rw;
has Str $.font_strikeout is rw;
has Str $.bg_color is rw;
has Bool $.hidden is rw;
has Str $.align is rw;
has Str $.valign is rw;
has Str $.font_charset is rw;
has Str $.num_format is rw;
has Str $.top_color is rw;
has Bool $.italic is rw;
has Str $.font_family is rw;
has Bool $.bold is rw;
has Str $.bottom_color is rw;
has Str $.size is rw;
has Str $.pattern is rw;
has Str $.font is rw;
has Str $.font_shadow is rw;
has Str $.text_justlast is rw;
has Str $.underline is rw;
=begin comment
Map any properties with aliases. The property name and the alis name
will return the same results with Bidirectional objects.
=end comment
has Bidirectional $!map = Bidirectional.new(:map(
font_encoding => <_font_encoding>;
));
=begin comment
Convert an attribute name such as is used by get_value and set_value
to a property name which keys the .object() hash.
=end comment
method !a2p (Str $candidate) {
return $candidate.substr(2) if $candidate.match(/^\$\W/);
}
=begin comment
Convert the property name to an attribute name that can be used with
get_value and set_value.
=end comment
method !p2a (Str $candidate, Str $prefix='$!') {
return $prefix ~ $candidate if !$candidate.match(/^\$\W/);
}
=begin comment
Given an attribute key, return its attribute alias.
=end comment
method alias (Str $candidate) {
return $!map.keyToAlias($candidate);
}
=begin comment
Given an attribute alias, return its attribute key.
=end comment
method key (Str $candidate) {
return $!map.aliasToKey($candidate);
}
=begin comment
Set the attribute values of the object. Any name supplied in the argument
must be a valid attribute and it must not be a private attribute.
=end comment
method set (%object) {
my #attributes = self.^attributes(:local);
for %object.kv -> $key, $value {
my $attribute = first { $_ ~~ /$key/ }, #attributes;
if $attribute && ($attribute.has_accessor) {
$attribute.set_value(self, $value);
}
}
}
=begin comment
Coverts a Format::Formatter object to a Hash containing only the public
attributes and values.
=end comment
method object (Format::Formatter:D $override?) {
my %answer = ();
my $victim = $override ?? $override !! self;
for $victim.^attributes(:local) -> $attribute {
next if !$attribute.has_accessor;
my $candidate = $attribute.get_value($victim);
my $property = self!a2p($attribute.name);
if $candidate {
%answer{$property} = $candidate;
}
}
return %answer;
}
=begin comment
Determine whether two Format::Formatter objects are the same. This comparison
is carried out against the .object() form of both objects, which excludes any
private attributes. If the two .object() values have the same keys and values,
True is returned; else False
=end comment
method same (Any $other) {
if $other.WHAT ∉ (Hash, Format::Formatter) {
X::TypeCheck.new(operation=>'Format::Formatter comparison',
got=>$other.WHAT, expected=>Format::Formatter).throw;
}
my $left = self.object;
my $right = $other ~~ Format::Formatter ?? $other.object !! $other;
my $answer = $left eqv $right;
return $answer;
}
}
=begin comment
Override === operator for Format::Formatter equivalence - this may have
unforseen results, and the .same() method should probably be used instead
The first form covers the case of $format1===$format2 -- both arguments
are converted to hashes using .object()
The second form covers the case of $format1==$hash2 -- the first argument
is converted to a hash using .object()
=end comment
multi sub infix:<===> (Format::Formatter:D $left, Format::Formatter:D $right) is export {
return $left.object eqv $right.object;
}
multi sub infix:<===> (Format::Formatter:D $left, Hash:D $right) is export {
return $left.object eqv $right;
}
I tried the following. I expect the custom operators to perform the operations I specify.
[2] > my $m = Format::Bidirectional.new(map=>{a => <_a>})
Format::Bidirectional.new(map => (my Any % = :a("_a")))
[3] > say $m.WHAT
(Bidirectional)
[3] > $m ~~ Format::Bidirectional
True
[4] > my $t = 'test'
test
[5] > $t ~~ Str
True
[6] > sub infix:<α> (Format::Bidirectional $name, Str $value) {
return $name.keyToAlias($value);
}
&infix:<α>
[7] > $m α $t
===SORRY!=== Error while compiling:
Two terms in a row
------> $m⏏ α $t
expecting any of:
infix
infix stopper
statement end
statement modifier
statement modifier loop
But using an example from the Raku documentation works:
sub infix:<:=:>( $a is rw, $b is rw ) {
($a, $b) = ($b, $a)
}
my ($num, $letter) = ('A', 3);
say $num; # OUTPUT: «A»
say $letter; # OUTPUT: «3»
# Swap two variables' values
$num :=: $letter;
say $num; # OUTPUT: «3»
say $letter; # OUTPUT: «A»
A
3
3
A
Trying to use the example substituting only my operations (though 'is rw' is spurious here), I get "cannot make assignment" (please note I exited the interaction shell and re-entered before attempting this):
[3] > sub infix:<:==:>($a is rw, $b is rw) { return $a.keyToAlias($b) }
&infix:<:==:>
[4] > $m :==: 'a'
===SORRY!=== Error while compiling:
Cannot make assignment out of := because list assignment operators are too fiddly
------> $m :==⏏: 'a'
I thought perhaps it was the unicode character I used (small alpha) or the class, so:
[0] > use lib "."
Nil
[1] > use Format
Nil
[2] > my $aa = {a=>'_a', b=>'_b'}
{a => _a, b => _b}
[3] > sub infix:<alias>($a, $b) { return $a=>$b }
&infix:<alias>
[4] > $aa alias 'a'
===SORRY!=== Error while compiling:
Two terms in a row
------> $aa⏏ alias 'a'
expecting any of:
infix
infix stopper
statement end
statement modifier
statement modifier loop
And to add insult to injury, thinking a literal term like "alias" might not work:
[4] > sub infix:<swap>( $a is rw, $b is rw ) {
($a, $b) = ($b, $a)
}
my ($num, $letter) = ('A', 3);
say $num; # OUTPUT: «A»
say $letter; # OUTPUT: «3»
# Swap two variables' values
$num swap $letter;
say $num; # OUTPUT: «3»
A
3
3
[4] >
Maybe it was the specific word I was using?
[4] > sub infix:<alias>( $a is rw, $b is rw ) {
($a, $b) = ($b, $a)
}
my ($num, $letter) = ('A', 3);
say $num; # OUTPUT: «A»
say $letter; # OUTPUT: «3»
# Swap two variables' values
$num alias $letter;
say $num; # OUTPUT: «3»
===SORRY!=== Error while compiling:
Two terms in a row
at line 10
------> $num⏏ swap $letter;
expecting any of:
infix
infix stopper
statement end
statement modifier
statement modifier loop
But then I exit and reinitialize:
[3] > sub infix:<alias>( $a is rw, $b is rw ) {
($a, $b) = ($b, $a)
}
my ($num, $letter) = ('A', 3);
say $num; # OUTPUT: «A»
say $letter; # OUTPUT: «3»
# Swap two variables' values
$num alias $letter;
say $num; # OUTPUT: «3»
A
3
3
And for good measure:
my $a = {a=>"_a", b=>"_b"}
{a => _a, b => _b}
[3] > sub infix:<α>( $a is rw, $b is rw ) {
($a, $b) = ($b, $a)
}
my ($num, $letter) = ('A', 3);
say $num; # OUTPUT: «A»
say $letter; # OUTPUT: «3»
# Swap two variables' values
$num α $letter;
say $num;
A
3
3
In the module file I define these custom operators. The subs do not work, the multi subs DO work:
sub infix:<α> (Format::Bidirectional $name, Str $value) is export {
return $name.keyToAlias($value);
}
sub infix:<κ> (Format::Bidirectional $name, Str $value) is export {
return $name.aliasToKey($value);
}
sub infix:<∨> (Format::Bidirectional $name, Str $value) is export {
return $name.either($value);
}
multi sub infix:<===> (Format::Formatter:D $left, Format::Formatter:D $right) is export {
return $left.object eqv $right.object;
}
multi sub infix:<===> (Format::Formatter:D $left, Hash:D $right) is export {
return $left.object eqv $right;
}
Need to create a function that implements the attached algorithm, to which all words are passed in the function arguments.
For example:
f ("dfd" dd "ddd");
My code:
fun main() {
var s = readLine();
var w = Array(128){0} //To mark characters from a word 1
var g = Array(128){0}//When we encounter a space, we add units from the first array to the corresponding elements of the second, zeroing them in the first.
if(s!=null)
{
for(c in s)
{
if(c.toInt() > 127 || c.toInt()<0) {
println("Input error, try again");
return;
}
//Checking for space.
if(c.toInt() != 32) w[c.toInt()] = 1;
else
for(k in 0..127)
{
if(w[k] == 1)
{
g[k] += 1;
w[k] = 0;
}
}
}
//For the last word, if there was no space after it.
for(k in 0..127)
{
if(w[k] == 1)
{
g[k] += 1;
w[k] = 0;
}
}
}
//Displaying matched characters to the screen
for(k in 0..127)
{
if(g[k]>1)
{
println(k.toChar());
}
}
}
This program searches for characters that match at least two words in a string
Example
input: hello world
output: lo
There's already utilities for these in Kotlin, I highly recommend you to read the docs before asking these type of questions.
The groupingBy should do what you want:
readLine()?.let { input ->
input.groupingBy { it }.eachCount()
.forEach { if (it.value > 1 && it.key != ' ') println(it.key) }
}
Is it possible to use multidispatch for the store method when using a Proxy? In the following minimal example, the code is called when storing an Int
my $foo := do {
my $bar = 1;
Proxy.new:
:FETCH( method { return $bar} ),
:STORE( method (Int $i) { $bar = $i } )
}
say $foo; # 1
$foo = 2;
say $foo; # 2
$foo = "3"; # error, need to pass an Int
But I'd like to handle the STORE differently if given, say, a Str. The work around I've found (other than doing a mega method with given/where is to create a multi sub inside of a block, and return the sub (because a multi method can't be referred to with &foo) with an dummy first parameter:
my $foo := do {
my $bar = 1;
Proxy.new:
:FETCH( method { return $bar} ),
:STORE(
do {
multi sub xyzzy ($, Int $i) { $bar = $i }
multi sub xyzzy ($, Str $i) { $bar = +$i + 1}
&xyzzy
}
)
}
say $foo; # 1
$foo = 2;
say $foo; # 2
$foo = "3";
say $foo; # 4
Is there a better way to do this (mainly for code clarity using method because sub feels...misleading)?
With regards to being misleading: the FETCH and STORE values expecte Callables, which could be either a method or a sub.
Getting back to the question, there is no direct way of doing this, but there is a better indirect way that may be clearer. You can do this by setting up the multi sub first, and then passing the proto as the parameter:
proto sub store(|) {*}
multi sub store(\self, Int) { say "Int" }
multi sub store(\self, Str) { say "Str" }
my $a := Proxy.new(
FETCH => -> $ { 42 },
STORE => &store,
);
say $a; # 42
$a = 42; # Int
$a = "foo"; # Str
And if you want to make the code shorter, but possibly less understandable, you can get rid of the proto (because it will be auto-generated for you) and the sub in the multi (because you can):
multi store(\self, Int) { say "Int" }
multi store(\self, Str) { say "Str" }
my $a := Proxy.new(
FETCH => -> $ { 42 },
STORE => &store,
);
say $a; # 42
$a = 42; # Int
$a = "foo"; # Str
Here's exercise 5.F.2 from 'A Book of Abstract Algebra' by Charles C Pinter:
Let G be the group {e, a, b, b^2, b^3, ab, ab^2, ab^3} whose
generators satisfy a^2 = e, b^4 = e, ba = ab^3. Write the table
of G. (G is called the dihedral group D4.)
Here's a little Perl 6 program which presents a solution:
sub generate(%eqs, $s)
{
my #results = ();
for %eqs.kv -> $key, $val {
if $s ~~ /$key/ { #results.push($s.subst(/$key/, $val)); }
if $s ~~ /$val/ { #results.push($s.subst(/$val/, $key)); }
}
for #results -> $result { take $result; }
my #arrs = #results.map({ gather generate(%eqs, $_) });
my $i = 0;
while (1)
{
for #arrs -> #arr { take #arr[$i]; }
$i++;
}
}
sub table(#G, %eqs)
{
printf " |"; for #G -> $y { printf "%-5s|", $y; }; say '';
printf "-----|"; for #G -> $y { printf "-----|"; }; say '';
for #G -> $x {
printf "%-5s|", $x;
for #G -> $y {
my $result = (gather generate(%eqs, "$x$y")).first(* (elem) #G);
printf "%-5s|", $result;
}
say ''
}
}
# ----------------------------------------------------------------------
# Pinter 5.F.2
my #G = <e a b bb bbb ab abb abbb>;
my %eqs = <aa e bbbb e ba abbb>; %eqs<e> = '';
table #G, %eqs;
Here's what the resulting table looks like:
Let's focus on these particular lines from generate:
my #arrs = #results.map({ gather generate(%eqs, $_) });
my $i = 0;
while (1)
{
for #arrs -> #arr { take #arr[$i]; }
$i++;
}
A recursive call to generate is made for each of the items in #results. Then we're effectively performing a manual 'zip' on the resulting sequences. However, Perl 6 has zip and the Z operator.
Instead of the above lines, I'd like to do something like this:
for ([Z] #results.map({ gather generate(%eqs, $_) })).flat -> $elt { take $elt; }
So here's the full generate using Z:
sub generate(%eqs, $s)
{
my #results = ();
for %eqs.kv -> $key, $val {
if $s ~~ /$key/ { #results.push($s.subst(/$key/, $val)); }
if $s ~~ /$val/ { #results.push($s.subst(/$val/, $key)); }
}
for #results -> $result { take $result; }
for ([Z] #results.map({ gather generate(%eqs, $_) })).flat -> $elt { take $elt; }
}
The issue with the Z version of generate is that it hangs...
So, my question is, is there a way to write generate in terms of Z?
Besides this core question, feel free to share alternative solutions to the exercise which explore and showcase Perl 6.
As another example, here's exercise 5.F.3 from the same book:
Let G be the group {e, a, b, b^2, b^3, ab, ab^2, ab^3} whose
generators satisfy a^4 = e, a^2 = b^2, ba = ab^3. Write the
table of G. (G is called the quaternion group.)
And the program above displaying the table:
As an aside, this program was converted from a version in C#. Here's how generate looks there using LINQ and a version of ZipMany courtesy of Eric Lippert.
static IEnumerable<string> generate(Dictionary<string,string> eqs, string s)
{
var results = new List<string>();
foreach (var elt in eqs)
{
if (new Regex(elt.Key).IsMatch(s))
results.Add(new Regex(elt.Key).Replace(s, elt.Value, 1));
if (new Regex(elt.Value).IsMatch(s))
results.Add(new Regex(elt.Value).Replace(s, elt.Key, 1));
}
foreach (var result in results) yield return result;
foreach (var elt in ZipMany(results.Select(elt => generate(eqs, elt)), elts => elts).SelectMany(elts => elts))
yield return elt;
}
The entire C# program: link.
[2022 update by #raiph. I just tested the first block of code in a recent Rakudo. The fourth example returned one result, 'abc', rather than none. This may be due to a new Raku design decision / roast improvement / trap introduced since this answer was last edited (in 2017), or a Rakudo bug. I'm not going to investigate; I just wanted to let readers know.]
Why your use of zip doesn't work
Your code assumes that [Z] ("reducing with the zip operator") can be used to get the transpose of a list-of-lists.
Unfortunately, this doesn't work in the general case.
It 'usually' works, but breaks on one edge case: Namely, when the list-of-lists is a list of exactly one list. Observe:
my #a = <a b c>, <1 2 3>, <X Y Z>; put [Z~] #a; # a1X b2Y c3Z
my #a = <a b c>, <1 2 3>; put [Z~] #a; # a1 b2 c3
my #a = <a b c>,; put [Z~] #a; # abc
my #a; put [Z~] #a; # abc <-- 2022 update
In the first two examples (3 and 2 sub-lists), you can see that the transpose of #a was returned just fine. The fourth example (0 sub-lists) does the right thing as well.
But the third example (1 sub-list) didn't print a b c as one would expect, i.e. it didn't return the transpose of #a in that case, but rather (it seems) the transpose of #a[0].
Sadly, this is not a Rakudo bug (in which case it could simply be fixed), but an unforseen interaction of two Perl 6 design decisions, namely:
The reduce meta-operator [ ] handles an input list with a single element by calling the operator it's applied to with one argument (said element).
In case you're wondering, an infix operator can be called with only one argument by invoking its function object: &infix:<Z>( <a b c>, ).
The zip operator Z and function zip (like other built-ins that accept nested lists), follows the so-called "single-argument rule" – i.e. its signature uses a single-argument slurpy parameter. This means that when it is called with a single argument, it will descend into it and consider its elements the actual arguments to use. (See also Slurpy conventions.)
So zip(<a b c>,) is treated as zip("a", "b", "c").
Both features provide some nice convenience in many other cases, but in this case their interaction regrettably poses a trap.
How to make it work with zip
You could check the number of elements of #arrs, and special-case the "exactly 1 sub-list" case:
my #arrs = #results.map({ gather generate(%eqs, $_) });
if #arrs.elems == 1 {
.take for #arrs[0][];
}
else {
.take for flat [Z] #arrs
}
The [] is a "zen slice" - it returns the list unchanged, but without the item container that the parent Array wrapped it in. This is needed because the for loop would consider anything wrapped in an item container as a single item and only do one iteration.
Of course, this if-else solution is not very elegant, which probably negates your reason for trying to use zip in the first place.
How to write the code more elegantly without zip
Refer to Christoph's answer.
It might be possible with a Z, but for my poor little brain, zipping recursively generated lazy lists is too much.
Instead, I did some other simplifications:
sub generate($s, %eqs) {
take $s;
# the given equations normalize the string, ie there's no need to apply
# the inverse relation
for %eqs.kv -> $k, $v {
# make copy of $s so we can use s/// instead of .subst
my $t = $s;
generate $t, %eqs
if $t ~~ s/$k/$v/;
}
}
sub table(#G, %eqs) {
# compute the set only once instead of implicitly on each call to (elem)
my $G = set #G;
# some code golfing
put ['', |#G]>>.fmt('%-5s|').join;
put '-----|' x #G + 1;
for #G -> $x {
printf '%-5s|', $x;
for #G -> $y {
printf '%-5s|', (gather generate("$x$y", %eqs)).first(* (elem) $G);
}
put '';
}
}
my #G = <e a b bb bbb ab abb abbb>;
# use double brackets so we can have empty strings
my %eqs = <<aa e bbbb e ba abbb e ''>>;
table #G, %eqs;
Here is a compact rewrite of generate that does bidirectional substitution, still without an explicit zip:
sub generate($s, %eqs) {
my #results = do for |%eqs.pairs, |%eqs.antipairs -> (:$key, :$value) {
take $s.subst($key, $value) if $s ~~ /$key/;
}
my #seqs = #results.map: { gather generate($_, %eqs) }
for 0..* -> $i { take .[$i] for #seqs }
}
Here's a version of generate that uses the approach demonstrated by smls:
sub generate(%eqs, $s)
{
my #results = ();
for %eqs.kv -> $key, $val {
if $s ~~ /$key/ { #results.push($s.subst(/$key/, $val)); }
if $s ~~ /$val/ { #results.push($s.subst(/$val/, $key)); }
}
for #results -> $result { take $result; }
my #arrs = #results.map({ gather generate(%eqs, $_) });
if #arrs.elems == 1 { .take for #arrs[0][]; }
else { .take for flat [Z] #arrs; }
}
I've tested it and it works on exercises 2 and 3.
As smls mentions in his answer, zip doesn't do what we were expecting when the given array of arrays only contains a single array. So, let's make a version of zip which does work with one or more arrays:
sub zip-many (#arrs)
{
if #arrs.elems == 1 { .take for #arrs[0][]; }
else { .take for flat [Z] #arrs; }
}
And now, generate in terms of zip-many:
sub generate(%eqs, $s)
{
my #results = ();
for %eqs.kv -> $key, $val {
if $s ~~ /$key/ { #results.push($s.subst(/$key/, $val)); }
if $s ~~ /$val/ { #results.push($s.subst(/$val/, $key)); }
}
for #results -> $result { take $result; }
zip-many #results.map({ gather generate(%eqs, $_) });
}
That looks pretty good.
Thanks smls!
smls suggests in a comment below that zip-many not invoke take, leaving that to generate. Let's also move flat from zip-many to generate.
The slimmed down zip-many:
sub zip-many (#arrs) { #arrs == 1 ?? #arrs[0][] !! [Z] #arrs }
And the generate to go along with it:
sub generate(%eqs, $s)
{
my #results;
for %eqs.kv -> $key, $val {
if $s ~~ /$key/ { #results.push($s.subst(/$key/, $val)); }
if $s ~~ /$val/ { #results.push($s.subst(/$val/, $key)); }
}
.take for #results;
.take for flat zip-many #results.map({ gather generate(%eqs, $_) });
}
Testing the keys and values separately seems a bit silly; your strings aren't really regexes, so there's no need for // anywhere in your code.
sub generate($s, #eqs) {
my #results = do for #eqs.kv -> $i, $equation {
take $s.subst($equation, #eqs[ $i +^ 1 ]) if $s.index: $equation
}
my #seqs = #results.map: { gather generate($_, #eqs) }
for 0..* -> $i { take .[$i] for #seqs }
}
Obviously with this version of generate you'll have to rewrite table to use #eqs instead of %eqs.
The program aims to use a loop to check if the index of a iterator variable meets certain criteria (i.g., index == 3). If find the desired index, return Some(123), else return None.
fn main() {
fn foo() -> Option<i32> {
let mut x = 5;
let mut done = false;
while !done {
x += x - 3;
if x % 5 == 0 {
done = true;
}
for (index, value) in (5..10).enumerate() {
println!("index = {} and value = {}", index, value);
if index == 3 {
return Some(123);
}
}
return None; //capture all other other possibility. So the while loop would surely return either a Some or a None
}
}
}
The compiler gives this error:
error[E0308]: mismatched types
--> <anon>:7:9
|
7 | while !done {
| ^ expected enum `std::option::Option`, found ()
|
= note: expected type `std::option::Option<i32>`
= note: found type `()`
I think the error source might be that a while loop evaluates to a (), thus it would return a () instead of Some(123). I don't know how to return a valid Some type inside a loop.
The value of any while true { ... } expression is always (). So the compiler expects your foo to return an Option<i32> but finds the last value in your foo body is ().
To fix this, you can add a return None outside the original while loop. You can also use the loop construct like this:
fn main() {
// run the code
foo();
fn foo() -> Option<i32> {
let mut x = 5;
loop {
x += x - 3;
for (index, value) in (5..10).enumerate() {
println!("index = {} and value = {}", index, value);
if index == 3 {
return Some(123);
}
}
if x % 5 == 0 {
return None;
}
}
}
}
The behaviour of while true { ... } statements is maybe a bit quirky and there have been a few requests to change it.