Related
I'm currently taking a class where we are learning Rust. I am running into a really weird issue and I'm not sure what is causing it.
So I have two vectors, and I'm looping through them to print their contents.
when I do the following, it prints out fine,
for index in 0..=delimited_record.capacity()-1{
let cell = delimited_record.get(index).unwrap();
println!("{}",cell);
}
the result is as follows
99
87.4
76
mike
73
95.5
100
gary
however, the following small change makes the 0th index of the second vector come out blank
for index in 0..=delimited_record.capacity()-1{
let cell = delimited_record.get(index).unwrap();
print!("{}",cell);
println!(" - {}",index);
}
the result is as follows
99 - 0
87.4 - 1
76 - 2
mike - 3
- 0
95.5 - 1
100 - 2
gary - 3
The thing is, if I try to do anything besides do a regular print, it'll come back blank. These values are all strings, so what I'm trying to do is convert these values into floats (not the names, just the numbers), but it keeps crashing whenever I try to parse the second array at the 0th index, and it seems it's because its blank for some reason.
Does anyone know what is causing this?
Edit
Here is the code in its entirety, Theres a lot of stuff commented out since i was trying to find out was was causing the issue
use std::{fs, ptr::null};
fn main() {
println!("Hello, world!");
println!("TEST");
let sg = StudentGrades{
name: String::from("mike"),
grades: vec![100.0,80.0,55.0,33.9,77.0]
//grades: vec!['A','B','C','A']
};
let mut cg = CourseGrades{
student_records: Vec::new()
};
cg.from_file("texttest.txt".to_owned());
}
struct StudentGrades{
name: String,
grades: Vec<f32>
}
impl StudentGrades{
fn average(&self)-> f32 {
let mut sum = 0.0;
for grade in self.grades.iter(){
sum += grade;
}
return sum / self.grades.capacity() as f32;
}
fn grade(&self) -> char {
let score = self.average();
let scure_truncated = score as i64 / 1;
match scure_truncated{
0..=59=> return 'F',
60..=69=> return 'D',
70..=79=> return 'C',
80..=89=> return 'B',
_=> return 'A',
}
}
}
struct CourseGrades{
student_records: Vec<StudentGrades>
}
impl CourseGrades{
fn from_file(&mut self, file_path:String){
let mut contents = fs::read_to_string(file_path)
.expect("Should have been able to read the file");
contents = contents.replace(" ","");
let student_rows: Vec<&str> = contents.rsplit('\n').collect();
for student_record in student_rows.iter(){
let mut student_grades:Vec<f32> = Vec::new();
let delimited_record: Vec<&str> = student_record.rsplit(",").collect();
//println!("{}",delimited_record.get(0).unwrap());
//delimited_record.iter().for_each(|x| println!("{}",x));
for index in 0..=delimited_record.len()-1{
//println!("{}",delimited_record.get(index).unwrap().parse::<f32>().unwrap_or_default());
//println!("{}",delimited_record.get(0).unwrap().parse::<i32>().unwrap_or_default());
//student_grades.push(delimited_record.get(index).unwrap().parse::<f32>().unwrap());
let cell = delimited_record.get(index).unwrap();
print!("{}",cell);
println!(" - {}",index);
//println!(" - {}", index < delimited_record.capacity()-1);
if index < delimited_record.len(){
//let grade = cell.parse::<f32>().unwrap();
//student_grades.push(cell.parse::<f32>().unwrap());
//student_grades.push(delimited_record.get(index).unwrap().parse::<f32>().unwrap());
}
else{
/*self.student_records.push(StudentGrades{
name:cell.parse::<String>().unwrap(),
grades:student_grades.to_owned()
});*/
}
}
}
}
}
the testtext.txt file is in the root of the project folder and is just a text file with the following contents
gary, 100, 95.5, 73
mike, 76, 87.4, 99
if I embed it directly, it works just fine, which makes me think there may be something weird when it reads the file
Your file has CR LF (\r\n, Windows-style) line endings, but you’re only splitting on \n. Your grade ends up ending with a CR. This is invisible when you println! it on its own (since the result is CR LF), but if you print something after it on the same line, the CR has returned the cursor to the start of the line and the second thing being printed will write over it.
7 73 73 73 73
^ ^ ^ ^ 9
^ ^
----------------------------------------
'7' '3' '\r' '\n' '9' …
7 73 73 3 -
^ ^ ^ ^ ^ ^
----------------------------------------
'7' '3' '\r' ' ' '-' …
One way to fix this is to strip all whitespace from every cell, including CR:
let cell = delimited_record.get(index).unwrap().trim();
And for debugging, consider formatting with "{:?}" instead of "{}", which will show a literal with invisible characters escaped instead of writing out whatever the string contains directly.
Let's assume, we want a bunch of constants, associating each square of a chess board with its coordinates, so we can use those constants in our Rust code.
One such definition could be:
#[allow(dead_code)]
const A1: (usize,usize) = (0, 0);
and there would be 64 of them.
Now, as a emacs user, I could generate the source code easily, for example with:
(dolist (col '(?A ?B ?C ?D ?E ?F ?G ?H))
(dolist (row '(?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8))
(insert "#[allow(dead_code)]")
(end-of-line)
(newline-and-indent)
(insert "const " col row ": (usize,usize) = ("
(format "%d" (- col ?A))
", "
(format "%d" (- row ?1))
");")
(end-of-line)
(newline-and-indent)))
With the drawback, that now my file just grew by 128 exceptionally boring lines.
In Common Lisp, I would solve this aspect, by defining myself a macro, for example:
(defmacro defconst-square-names ()
(labels ((square-name (row col)
(intern
(format nil "+~C~D+"
(code-char (+ (char-code #\A) col))
(+ row 1))))
(one-square (row col)
`(defconstant ,(square-name row col)
(cons ,row ,col))))
`(eval-when (:compile-toplevel :load-toplevel :execute)
,#(loop
for col below 8
appending
(loop for row below 8
collecting (one-square row col))))))
(defconst-square-names) ;; nicer packaging of those 64 boring lines...
Now, the question arises, of course,
if Rust macro system is able to accomplish this?
can someone show such a macro?
I read, you need to put such Rust macro into a separate crate or whatnot?!
UPDATE
#aedm pointed me with the comment about seq-macro crate to my first attempt to get it done. But unfortunately, from skimming over various Rust documents about macros, I still don't know how to define and call compile time functions from within such a macro:
fn const_name(index:usize) -> String {
format!("{}{}",
char::from_u32('A' as u32
+ (index as u32 % 8)).unwrap()
, index / 8)
}
seq!(index in 0..64 {
#[allow(dead_code)]
const $crate::const_name(index) : (usize,usize) = ($(index / 8), $(index %8));
});
In my Common Lisp solution, I just defined local functions within the macro to get such things done. What is the Rust way?
Here's one way to do it only with macro_rules! ("macros by example") and the paste crate (to construct the identifiers). It's not especially elegant, but it is fairly short and doesn't require you to write a proc-macro crate.
It needs to be invoked with all of the involved symbols since macro_rules! can't do arithmetic. (Maybe seq-macro would help some with that, but I'm not familiar with it.)
use paste::paste;
macro_rules! board {
// For each column, call column!() passing the details of that column
// and all of the rows. (This can't be done in one macro because macro
// repetition works like "zip", not like "cartesian product".)
( ($($cols:ident $colnos:literal),*), $rows:tt ) => {
$( column!($cols, $colnos, $rows); )*
};
}
/// Helper for board!
macro_rules! column {
( $col:ident, $colno:literal, ($($rows:literal),*) ) => {
$(
paste! {
// [< >] are special brackets that tell the `paste!` macro to
// paste together all the pieces appearing within them into
// a single identifier.
#[allow(dead_code)]
const [< $col $rows >]: (usize, usize) = ($colno, $rows - 1);
}
)*
};
}
board!((A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7), (1, 2, 3, 4, 5, 6, 7, 8));
fn main() {
dbg!(A1, A8, H1, H8);
}
I've noticed that in Rust, we can't use the byte notation for values larger than 128, that is
let x = "\x01\x17\x7f"
is fine since all chars are < 128, but
let x = "\x01\x17\x80"
will fail since \x80 = 128.
Is there any way to still write string-like objects in that format?
Above 127 you enter the realm of Unicode and must use the \u{codepoint} escape sequence:
let x = "\u{80}";
Note however that 0x80 by itself isn't a valid byte in a UTF-8 string, so this turns out as two bytes:
let x = "\u{80}";
for b in x.bytes() {
println!("{:X}", b);
}
prints
C2
80
If you instead need the value 0x80, you can't use a string and must use a byte slice:
fn main() {
let x = b"\x80";
for b in x {
println!("{:X}", b);
}
}
prints
80
I am writing a macro to parse some structured text into tuples, line by line. Most parts work now, but I am stuck at forming a tuple by extracting/converting Strings from a vector.
// Reading Tuple from a line
// Example : read_tuple( "1 ab 3".lines()
// ,(i32, String, i32))
// Expected : (1, "ab", 3)
// Note:: you can note use str
macro_rules! read_tuple {
(
$lines :ident , ( $( $t :ty ),* )
)
=> {{
let l = ($lines).next().unwrap();
let ws = l.trim().split(" ").collect::<Vec<_>>();
let s : ( $($t),* ) = (
// for w in ws {
// let p = w.parse().unwrap();
// ( p) ,
// }
ws[0].parse().unwrap(),
ws[1].parse().unwrap(),
//...
ws[2].parse().unwrap(),
// Or any way to automatically generate these statments?
);
s
}}
}
fn main() {
let mut _x = "1 ab 3".lines();
let a = read_tuple!( _x, (i32, String, i32));
print!("{:?}",a);
}
How can I iterate through ws and return the tuple within this macro?
You can try here
A tuple is a heterogeneous collection; each element may be of a different type. And in your example, they are of different types, so each parse method is needing to produce a different type. Therefore pure runtime iteration is right out; you do need all the ws[N].parse().unwrap() statements expanded.
Sadly there is not at present any way of writing out the current iteration of a $(…)* (though it could be simulated with a compiler plugin). There is, however, a way that one can get around that: blending run- and compile-time iteration. We use iterators to pull out the strings, and the macro iteration expansion (ensuring that $t is mentioned inside the $(…) so it knows what to repeat over) to produce the right number of the same lines. This also means we can avoid using an intermediate vector as we are using the iterator directly, so we win all round.
macro_rules! read_tuple {
(
$lines:ident, ($($t:ty),*)
) => {{
let l = $lines.next().unwrap();
let mut ws = l.trim().split(" ");
(
$(ws.next().unwrap().parse::<$t>().unwrap(),)*
)
}}
}
A minor thing to note is how I changed ),* to ,)*; this means that you will get (), (1,), (1, 2,), (1, 2, 3,), &c. instead of (), (1), (1, 2), (1, 2, 3)—the key difference being that a single-element tuple will work (though you’ll still sadly be writing read_tuple!(lines, (T))).
I'd like to convert an Int in Swift to a String with leading zeros. For example consider this code:
for myInt in 1 ... 3 {
print("\(myInt)")
}
Currently the result of it is:
1
2
3
But I want it to be:
01
02
03
Is there a clean way of doing this within the Swift standard libraries?
Assuming you want a field length of 2 with leading zeros you'd do this:
import Foundation
for myInt in 1 ... 3 {
print(String(format: "%02d", myInt))
}
output:
01
02
03
This requires import Foundation so technically it is not a part of the Swift language but a capability provided by the Foundation framework. Note that both import UIKit and import Cocoa include Foundation so it isn't necessary to import it again if you've already imported Cocoa or UIKit.
The format string can specify the format of multiple items. For instance, if you are trying to format 3 hours, 15 minutes and 7 seconds into 03:15:07 you could do it like this:
let hours = 3
let minutes = 15
let seconds = 7
print(String(format: "%02d:%02d:%02d", hours, minutes, seconds))
output:
03:15:07
With Swift 5, you may choose one of the three examples shown below in order to solve your problem.
#1. Using String's init(format:_:) initializer
Foundation provides Swift String a init(format:_:) initializer. init(format:_:) has the following declaration:
init(format: String, _ arguments: CVarArg...)
Returns a String object initialized by using a given format string as a template into which the remaining argument values are substituted.
The following Playground code shows how to create a String formatted from Int with at least two integer digits by using init(format:_:):
import Foundation
let string0 = String(format: "%02d", 0) // returns "00"
let string1 = String(format: "%02d", 1) // returns "01"
let string2 = String(format: "%02d", 10) // returns "10"
let string3 = String(format: "%02d", 100) // returns "100"
#2. Using String's init(format:arguments:) initializer
Foundation provides Swift String a init(format:arguments:) initializer. init(format:arguments:) has the following declaration:
init(format: String, arguments: [CVarArg])
Returns a String object initialized by using a given format string as a template into which the remaining argument values are substituted according to the user’s default locale.
The following Playground code shows how to create a String formatted from Int with at least two integer digits by using init(format:arguments:):
import Foundation
let string0 = String(format: "%02d", arguments: [0]) // returns "00"
let string1 = String(format: "%02d", arguments: [1]) // returns "01"
let string2 = String(format: "%02d", arguments: [10]) // returns "10"
let string3 = String(format: "%02d", arguments: [100]) // returns "100"
#3. Using NumberFormatter
Foundation provides NumberFormatter. Apple states about it:
Instances of NSNumberFormatter format the textual representation of cells that contain NSNumber objects and convert textual representations of numeric values into NSNumber objects. The representation encompasses integers, floats, and doubles; floats and doubles can be formatted to a specified decimal position.
The following Playground code shows how to create a NumberFormatter that returns String? from a Int with at least two integer digits:
import Foundation
let formatter = NumberFormatter()
formatter.minimumIntegerDigits = 2
let optionalString0 = formatter.string(from: 0) // returns Optional("00")
let optionalString1 = formatter.string(from: 1) // returns Optional("01")
let optionalString2 = formatter.string(from: 10) // returns Optional("10")
let optionalString3 = formatter.string(from: 100) // returns Optional("100")
For left padding add a string extension like this:
Swift 5.0 +
extension String {
func padLeft(totalWidth: Int, with byString: String) -> String {
let toPad = totalWidth - self.count
if toPad < 1 {
return self
}
return "".padding(toLength: toPad, withPad: byString, startingAt: 0) + self
}
}
Using this method:
for myInt in 1...3 {
print("\(myInt)".padLeft(totalWidth: 2, with: "0"))
}
Swift 3.0+
Left padding String extension similar to padding(toLength:withPad:startingAt:) in Foundation
extension String {
func leftPadding(toLength: Int, withPad: String = " ") -> String {
guard toLength > self.characters.count else { return self }
let padding = String(repeating: withPad, count: toLength - self.characters.count)
return padding + self
}
}
Usage:
let s = String(123)
s.leftPadding(toLength: 8, withPad: "0") // "00000123"
Swift 5
#imanuo answers is already great, but if you are working with an application full of number, you can consider an extension like this:
extension String {
init(withInt int: Int, leadingZeros: Int = 2) {
self.init(format: "%0\(leadingZeros)d", int)
}
func leadingZeros(_ zeros: Int) -> String {
if let int = Int(self) {
return String(withInt: int, leadingZeros: zeros)
}
print("Warning: \(self) is not an Int")
return ""
}
}
In this way you can call wherever:
String(withInt: 3)
// prints 03
String(withInt: 23, leadingZeros: 4)
// prints 0023
"42".leadingZeros(2)
// prints 42
"54".leadingZeros(3)
// prints 054
Using Swift 5’s fancy new extendible interpolation:
extension DefaultStringInterpolation {
mutating func appendInterpolation(pad value: Int, toWidth width: Int, using paddingCharacter: Character = "0") {
appendInterpolation(String(format: "%\(paddingCharacter)\(width)d", value))
}
}
let pieCount = 3
print("I ate \(pad: pieCount, toWidth: 3, using: "0") pies") // => `I ate 003 pies`
print("I ate \(pad: 1205, toWidth: 3, using: "0") pies") // => `I ate 1205 pies`
in Xcode 8.3.2, iOS 10.3
Thats is good to now
Sample1:
let dayMoveRaw = 5
let dayMove = String(format: "%02d", arguments: [dayMoveRaw])
print(dayMove) // 05
Sample2:
let dayMoveRaw = 55
let dayMove = String(format: "%02d", arguments: [dayMoveRaw])
print(dayMove) // 55
The other answers are good if you are dealing only with numbers using the format string, but this is good when you may have strings that need to be padded (although admittedly a little diffent than the question asked, seems similar in spirit). Also, be careful if the string is longer than the pad.
let str = "a str"
let padAmount = max(10, str.count)
String(repeatElement("-", count: padAmount - str.count)) + str
Output "-----a str"
The below code generates a 3 digits string with 0 padding in front:
import Foundation
var randomInt = Int.random(in: 0..<1000)
var str = String(randomInt)
var paddingZero = String(repeating: "0", count: 3 - str.count)
print(str, str.count, paddingZero + str)
Output:
5 1 005
88 2 088
647 3 647
Swift 4* and above you can try this also:
func leftPadding(valueString: String, toLength: Int, withPad: String = " ") -> String {
guard toLength > valueString.count else { return valueString }
let padding = String(repeating: withPad, count: toLength - valueString.count)
return padding + valueString
}
call the function:
leftPadding(valueString: "12", toLength: 5, withPad: "0")
Output:
"00012"
Details
Xcode 9.0.1, swift 4.0
Solutions
Data
import Foundation
let array = [0,1,2,3,4,5,6,7,8]
Solution 1
extension Int {
func getString(prefix: Int) -> String {
return "\(prefix)\(self)"
}
func getString(prefix: String) -> String {
return "\(prefix)\(self)"
}
}
for item in array {
print(item.getString(prefix: 0))
}
for item in array {
print(item.getString(prefix: "0x"))
}
Solution 2
for item in array {
print(String(repeatElement("0", count: 2)) + "\(item)")
}
Solution 3
extension String {
func repeate(count: Int, string: String? = nil) -> String {
if count > 1 {
let repeatedString = string ?? self
return repeatedString + repeate(count: count-1, string: repeatedString)
}
return self
}
}
for item in array {
print("0".repeate(count: 3) + "\(item)")
}
Unlike the other answers that use a formatter, you can also just add an "0" text in front of each number inside of the loop, like this:
for myInt in 1...3 {
println("0" + "\(myInt)")
}
But formatter is often better when you have to add suppose a designated amount of 0s for each seperate number. If you only need to add one 0, though, then it's really just your pick.