Why can't I call gen_range with two i32 arguments? - function

I have this code but it doesn't compile:
use rand::Rng;
use std::io;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(0, 101);
println!("The secret number is: {}", secret_number);
println!("Please input your guess.");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
println!("You guessed: {}", guess);
}
Compile error:
error[E0061]: this function takes 1 argument but 2 arguments were supplied
--> src/main.rs:7:44
|
7 | let secret_number = rand::thread_rng().gen_range(0, 101);
| ^^^^^^^^^ - --- supplied 2 arguments
| |
| expected 1 argument

The gen_range method expects a single Range argument, not two i32 arguments, so change:
let secret_number = rand::thread_rng().gen_range(0, 101);
to:
let secret_number = rand::thread_rng().gen_range(0..101);
And it will compile and work. Note: the method signature was updated in version 0.8.0 of the rand crate, in all prior versions of the crate your code should work as-is.

run cargo update
It will list the version for rand.
update the version in Cargo.toml under dependencies
[dependencies]
rand = "0.8.3"
hopefully this solves the above problem

Related

How to include a integer while throwing a failure exception in OCaml

I have this function
let f = function
| 1 -> "a"
| 2 -> "b"
| _ -> failwith "Argument should be less than 3 and more than 0 but it was found to be x"
How do I set the value of x here equal to the function's input?
You can use the standard library function sprintf present in the Printf module.
| x -> failwith (Printf.sprintf "Argument should be ... but it was %d" x)
Although, I would recommend you to use invalid_arg instead of failwith since you are throwing the exception due to an invalid argument.
Check out this page of the OCaml documentation.
If you wish to handle that exception, parsing that int out of the error message might be annoying.
Defining your own exception is something you should learn eventually when learning OCaml because it gives you the flexibility to pass any information you need in the exception. Here's a simple example:
exception Out_of_range of {
range_start : int;
range_end : int;
received : int
}
Now, you can define your function as:
let f = function
| 1 -> "a"
| 2 -> "b"
| n -> raise (Out_of_range { range_start=1; range_end=2; received=n })
And when calling it, if you want to raise a Failure with a formatted string:
let n = read_int () in
try
f n
with
| Out_of_range {range_start=s; range_end=e; received=n} ->
failwith (Format.sprintf "Argument should be between %d and %d but it was found to be %d" s e n)

Is this possible to set a default value of a function without loosing the polymorphic type of an argument in ocaml [duplicate]

This question already has answers here:
Force a broader type for optional argument with more restrictive default value
(3 answers)
Closed 1 year ago.
I have this function to add logs in a file :
let log_datas f file (datas: 'a list) =
let oc = open_out file.file_name in
List.iter (fun x -> Printf.fprintf oc "%s" ## f x) datas;
close_out oc
let () = let f = string_of_int in log_datas f {file_name="log"} [1;2]
Which works.
I tried to make it by default accepting string list as argument :
let log_datas ?(f:'a -> string = fun x -> x^"\n") file (datas: 'a list) =
let oc = open_out file.file_name in
List.iter (fun x -> Printf.fprintf oc "%s" ## f x) datas;
close_out oc
but when I try
let () = let f = string_of_int in log_datas ~f {file_name="log"} [1;2]
I get a type error
23 | let () = let f = string_of_int in log_datas ~f {file_name="log"} [1;2]
^
Error: This expression has type int -> string
but an expression was expected of type string -> string
Type int is not compatible with type string
An obvious solution would be to make 2 function, one with no f argument and one with a f argument. But I was wondering, is there any other workaround possible ?
No, it is not possible, you have to specify both parameters to keep it polymorphic. Basically, your example could be distilled to,
let log ?(to_string=string_of_int) data =
print_endline (to_string data)
If OCaml would keep it polymorphic then the following would be allowed,
log "hello"
and string_of_int "hello" is not well-typed.
So you have to keep both parameters required, e.g.,
let log to_string data =
print_endline (to_string data)
I would also suggest looking into the Format module and defining your own polymorphic function that uses format specification to define how data of different types are written, e.g.,
let log fmt =
Format.kasprintf print_endline fmt
Substitute print_endline with our own logging facility. The log function could be used as printf, e.g.,
log "%s %d" "hello" 42

How to read a CSV that includes Chinese characters in Rust?

When I read a CSV file that includes Chinese characters using the csv crate, it has a error.
fn main() {
let mut rdr =
csv::Reader::from_file("C:\\Users\\Desktop\\test.csv").unwrap().has_headers(false);
for record in rdr.decode() {
let (a, b): (String, String) = record.unwrap();
println!("a:{},b:{}", a, b);
}
thread::sleep_ms(500000);
}
The error:
Running `target\release\rust_Work.exe`
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Decode("Could not convert bytes \'FromUtf8Error { bytes: [208, 213, 195, 251], error: Utf8Error { va
lid_up_to: 0 } }\' to UTF-8.")', ../src/libcore\result.rs:788
note: Run with `RUST_BACKTRACE=1` for a backtrace.
error: Process didn't exit successfully: `target\release\rust_Work.exe` (exit code: 101)
test.csv:
1. 姓名 性别 年纪 分数 等级
2. 小二 男 12 88 良好
3. 小三 男 13 89 良好
4. 小四 男 14 91 优秀
I'm not sure what could be done to make the error message more clear:
Decode("Could not convert bytes 'FromUtf8Error { bytes: [208, 213, 195, 251], error: Utf8Error { valid_up_to: 0 } }' to UTF-8.")
FromUtf8Error is documented in the standard library, and the text of the error says "Could not convert bytes to UTF-8" (although there's some extra detail in the middle).
Simply put, your data isn't in UTF-8 and it must be. That's all that the Rust standard library (and thus most libraries) really deal with. You will need to figure out what encoding it is in and then find some way of converting from that to UTF-8. There may be a crate to help with either of those cases.
Perhaps even better, you can save the file as UTF-8 from the beginning. Sadly, it's relatively common for people to hit this issue when using Excel, because Excel does not have a way to easily export UTF-8 CSV files. It always writes a CSV file in the system locale encoding.
I have a way to solve it. Thanks all.
extern crate csv;
extern crate rustc_serialize;
extern crate encoding;
use encoding::{Encoding, EncoderTrap, DecoderTrap};
use encoding::all::{GB18030};
use std::io::prelude::*;
fn main() {
let path = "C:\\Users\\Desktop\\test.csv";
let mut f = File::open(path).expect("cannot open file");
let mut reader: Vec<u8> = Vec::new();
f.read_to_end(&mut reader).expect("can not read file");
let mut chars = String::new();
GB18030.decode_to(&mut reader, DecoderTrap::Ignore, &mut chars);
let mut rdr = csv::Reader::from_string(chars).has_headers(true);
for row in rdr.decode() {
let (x, y, r): (String, String, String) = row.unwrap();
println!("({}, {}): {:?}", x, y, r);
}
}
output:
Part 1: Read Unicode (Chinese or not) characters:
The easiest way to achieve your goal is to use the read_to_string function that mutates the String you pass to it, appending the Unicode content of your file to that passed String:
use std::io::prelude::*;
use std::fs::File;
fn main() {
let mut f = File::open("file.txt").unwrap();
let mut buffer = String::new();
f.read_to_string(&mut buffer);
println!("{}", buffer)
}
Part 2: Parse a CSV file, its delimiter being a ',':
extern crate regex;
use regex::Regex;
use std::io::prelude::*;
use std::fs::File;
fn main() {
let mut f = File::open("file.txt").unwrap();
let mut buffer = String::new();
let delimiter = ",";
f.read_to_string(&mut buffer);
let modified_buffer = buffer.replace("\n", delimiter);
let mut regex_str = "([^".to_string();
regex_str.push_str(delimiter);
regex_str.push_str("]+)");
let mut final_part = "".to_string();
final_part.push_str(delimiter);
final_part.push_str("?");
regex_str.push_str(&final_part);
let regex_str_copy = regex_str.clone();
regex_str.push_str(&regex_str_copy);
regex_str.push_str(&regex_str_copy);
let re = Regex::new(&regex_str).unwrap();
for cap in re.captures_iter(&modified_buffer) {
let (s1, s2, dist): (String, String, usize) =
(cap[1].to_string(), cap[2].to_string(), cap[3].parse::<usize>().unwrap());
println!("({}, {}): {}", s1, s2, dist);
}
}
Sample input and output here

Can't define an exception only in a mli file

Ok, this is mostly about curiosity but I find it too strange.
Let's suppose I have this code
sig.mli
type t = A | B
main.ml
let f =
let open Sig in
function A | B -> ()
If I compile, everything will work.
Now, let's try to modify sig.mli
sig.mli
type t = A | B
exception Argh
and main.ml
main.ml
let f =
let open Sig in
function
| A -> ()
| B -> raise Argh
And let's try to compile it :
> ocamlc -o main sig.mli main.ml
File "main.ml", line 1:
Error: Error while linking main.cmo:
Reference to undefined global `Sig'
Well, is it just because I added the exception ? Maybe it means that exceptions are like functions or modules, you need a proper implementation.
But then, what if I write
main.ml
let f =
let open Sig in
function A | B -> ()
And try to compile ?
> ocamlc -o main sig.mli main.ml
>
It worked ! If I don't use the exception, it compiles !
There is no reason to this behaviour, right ? (I tested it on different compilers, 3.12.0, 4.00.0, 4.02.3 and 4.03.0 and all of them gave the same error)
Unlike variants, exception is not a pure type and requires its implementation in .ml file. Compile the following code with ocamlc -dlambda -c x.ml:
let x = Exit
-- the output --
(setglobal X!
(seq (opaque (global Pervasives!))
(let (x/1199 = (field 2 (global Pervasives!)))
(pseudo _none_(1)<ghost>:-1--1 (makeblock 0 x/1199)))))
You can see (let (x/1999 = (field 2 (global Pervasives!))).. which means assigning the value stored in the 2nd position of module Pervasives. This is the value of Exit. Exceptions have their values and therefore need .ml.
Variants do not require implementation. It is since their values can be constructed purely from their type information: constructors' tag integers. We cannot assign tag integers to exceptions (and their generalized version, open type constructors) since they are openly defined. Instead they define values for their identification in .ml.
To get an implementation of the exception, you need sig.ml. A .mli file is an interface file, a .ml file is an implementation file.
For this simple example you could just rename sig.mli to sig.ml:
$ cat sig.ml
type t = A | B
exception Argh
$ cat main.ml
let f =
let open Sig in
function
| A -> ()
| B -> raise Argh
$ ocamlc -o main sig.ml main.ml
I don't see a problem with this behavior, though it would be nice not to have to duplicate types and exceptions between .ml and .mli files. The current setup has the advantage of being simple and explicit. (I'm not a fan of compilers being too clever and doing things behind my back.)

Printing stack traces

I have a very short test file:
let print_backtrace () = try raise Not_found with
Not_found -> Printexc.print_backtrace stdout;;
let f () = print_backtrace (); Printf.printf "this is to make f non-tail-recursive\n";;
f ();
I compile and run:
% ocamlc -g test.ml
% OCAMLRUNPARAM=b ./a.out
Raised at file "test.ml", line 1, characters 35-44
this is to make f non-tail-recursive
Why isn't f listed in the stack trace? How can I write a function that will print a stack trace of the location it's called from?
The documentation for Printexc.print_backtrace says:
The backtrace lists the program locations where the most-recently raised exception was raised and where it was propagated through function calls.
It actually seems to be doing the right thing. The exception hasn't been propagated back through f.
If I move the call to Printexc.print_backtrace outside the call to f, I see a full backtrace.
$ cat test2.ml
let print_backtrace () = raise Not_found
let f () = let res = print_backtrace () in res ;;
try f () with Not_found -> Printexc.print_backtrace stdout
$ /usr/local/ocaml312/bin/ocamlc -g test2.ml
$ OCAMLRUNPARAM=b a.out
Raised at file "test2.ml", line 1, characters 31-40
Called from file "test2.ml", line 3, characters 21-39
Called from file "test2.ml", line 5, characters 4-8
Here is the code to do what I suggested. I recommend using ocamldebug if at all possible, this code is much too tricky. But it works on my system for this simple example.
let print_backtrace () =
match Unix.fork () with
| 0 -> raise Not_found
| pid -> let _ = Unix.waitpid [] pid in ()
let f () =
begin
print_backtrace ();
Printf.printf "after the backtrace\n";
end
;;
f ()
Here is a test run.
$ /usr/local/ocaml312/bin/ocamlc unix.cma -g test3.ml
$ OCAMLRUNPARAM=b a.out
Fatal error: exception Not_found
Raised at file "test3.ml", line 3, characters 17-26
Called from file "test3.ml", line 8, characters 4-22
Called from file "test3.ml", line 14, characters 0-4
after the backtrace
I realized that because of the uncaught exception, you don't really have any control over the way the child process exits. That's one reason this code is much too tricky. Please don't blame me if it doesn't work for you, but I hope it does prove useful.
I tested the code on Mac OS X 10.6.8 using OCaml 3.12.0.
Best regards,