rust echo(1)
This commit is contained in:
		
							parent
							
								
									933c3d8c5e
								
							
						
					
					
						commit
						6ad17fef83
					
				
							
								
								
									
										10
									
								
								echo/echo.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								echo/echo.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| use std::env::args; | ||||
| 
 | ||||
| fn main() { // cannot do non UTF-8 strings as far as I know
 | ||||
|     if args().collect::<Vec<String>>().len() >= 2 { | ||||
|         for argument in args().skip(1) { | ||||
|             print!("{} ", argument); | ||||
|         } | ||||
|     } | ||||
|     print!("\n"); | ||||
| } | ||||
							
								
								
									
										5
									
								
								false/false.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								false/false.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| use std::process::ExitCode; | ||||
| 
 | ||||
| fn main() -> ExitCode { | ||||
|     ExitCode::from(1) | ||||
| } | ||||
| @ -1925,6 +1925,843 @@ If I don't, escalate the issue to the host of this site (this can also be found | ||||
| </HTML> | ||||
| 
 | ||||
| 
 | ||||
| /blah/2023-10-31.html | ||||
| 
 | ||||
| : trinity writes a rust hello world | ||||
| 
 | ||||
| Where I now find myself living (though to say I live here would be a lie) I am | ||||
| surrounded by a couple of the smartest people I know, and through some days of | ||||
| wearing me down I am donning the programmer socks and writing a Rust Hello | ||||
| World program. | ||||
| 
 | ||||
| I am now actually wearing thigh highs. | ||||
| # apk add rust | ||||
| 
 | ||||
| I don't actually know how to get the Rust build system going but this seems | ||||
| like the best option so I'll go with this which is already packaged for | ||||
| Chimera. | ||||
| 
 | ||||
| Oh, I'll need cargo(1) too. | ||||
| # apk add cargo | ||||
| 
 | ||||
| One of my friends built the Rust book PDF for me which is nice because I can | ||||
| consult it on my tablet while programming on the laptop. | ||||
| 
 | ||||
| >Foreword | ||||
| >It wasn't always so clear, but the Rust programming language is fundamentally | ||||
| >about *empowerment*... | ||||
| 
 | ||||
| Okay, I get why so many chan-types are so against Rust. But seeing how people | ||||
| who know Rust use Rust I am sort of starting to get it. It's a high level | ||||
| language that can be used well for systems programming, basically? | ||||
| 
 | ||||
| >To check whether you have Rust installed correctly, open a shell and enter | ||||
| >this line: | ||||
| $ rustc --version | ||||
| Okay. | ||||
| rustc 1.73.0 (cc66ad468 2023-10-03) (Chimera Linux) | ||||
| Awesome! | ||||
| 
 | ||||
| I don't have rustup so I can't read the Rust docs but I'll probably be around a | ||||
| web browser when programming so I think it's fine? | ||||
| 
 | ||||
| Rust wants me to make a Hello, World! to start, but that's not super practical | ||||
| code for me. I think I'm gonna start smaller and make a true(1) implementation. | ||||
| 
 | ||||
| ```rs | ||||
| fn main() { | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| Works. | ||||
| 
 | ||||
| ```rs | ||||
| ``` | ||||
| 
 | ||||
| Does not work; there's no `main` function so the program doesn't know how to | ||||
| execute: | ||||
| 
 | ||||
| error[E0601]: `main` function not found in crate `r#true` | ||||
|   | | ||||
|   = note: consider adding a `main` function to `true.rs` | ||||
| 
 | ||||
| error: aborting due to previous error | ||||
| 
 | ||||
| For more information about this error, try `rustc --explain E0601`. | ||||
| 
 | ||||
| I really like the `rustc --explain` thing, this reminds me of Shellcheck. | ||||
| Compare to the clang error message when compiling the same file: | ||||
| 
 | ||||
| ld: error: undefined symbol: main | ||||
| >>> referenced by crt1.c:18 (../crt/crt1.c:18) | ||||
| >>>               /lib/Scrt1.o:(_start_c) | ||||
| >>> referenced by crt1.c:18 (../crt/crt1.c:18) | ||||
| >>>               /lib/Scrt1.o:(_start_c) | ||||
| clang-16: error: linker command failed with exit code 1 (use -v to see invocati | ||||
| on) | ||||
| 
 | ||||
| There's a lot going on here that the beginner (or even proficient C programmer) | ||||
| doesn't know and doesn't know how to start to know. | ||||
| 
 | ||||
| Alright, what about this: | ||||
| 
 | ||||
| ```rs | ||||
| fn main(); | ||||
| ``` | ||||
| 
 | ||||
| error: free function without a body | ||||
|  --> true.rs:1:1 | ||||
|   | | ||||
| 1 | fn main(); | ||||
|   | ^^^^^^^^^- | ||||
|   |          | | ||||
|   |          help: provide a definition for the function: `{ <body> }` | ||||
| 
 | ||||
| error: aborting due to previous error | ||||
| 
 | ||||
| Okay, so `fn main() { }` seems to be the simplest way to do this. How do I | ||||
| return an exit code explicitly, though, so I can make a false(1) | ||||
| implementation? | ||||
| 
 | ||||
| It was at this point one of the people I know who knows Rust came by and I told | ||||
| them how I was coming along and they were really supportive of my very meager | ||||
| progress. | ||||
| 
 | ||||
| I found some stuff here: | ||||
| https://doc.rust-lang.org/std/process/struct.ExitCode.html | ||||
| 
 | ||||
| So instead of understanding everything that's happening I'll try just plugging | ||||
| some code in, StackOverflow style: | ||||
| 
 | ||||
| ```rs | ||||
| use std::process::ExitCode; | ||||
| 
 | ||||
| fn main() -> ExitCode { | ||||
| 	ExitCode::from(0) | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| TRIN: Can I name you in my blog? Or should I keep saying "it was at this point | ||||
|       one of the people with which I'm staying walked through on its pacing | ||||
|       route"? | ||||
| MARS: You can say Mars, that's fine. | ||||
| 
 | ||||
| TRIN:               So you can put a constant on the last line of a function | ||||
|                     without a trailing semicolon to return that value? | ||||
| MARS [paraphrased]: Yeah. It's less to say, "return that value" than it is to | ||||
|                     say "this function has this value". Rust is a functional | ||||
|                     language disguised as a procedural language. | ||||
| 
 | ||||
| Okay, that fucks. ExitCode has a SUCCESS constant I could also use, meaning the | ||||
| equivalent to C's `E_OK` or whatever the constant provided by stdio.h is, but | ||||
| I'm wary about using a library-defined constant less it changes because POSIX | ||||
| does not change (much). So I think this is a good Rust true(1) implementation. | ||||
| It can be found in src/true/true.rs. And src/false/false.rs: | ||||
| 
 | ||||
| ```rs | ||||
| use std::process::ExitCode; | ||||
| 
 | ||||
| fn main() -> ExitCode { | ||||
| 	ExitCode::from(1) | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| I just had supper which was delicious, vegan hot dogs and some macaroni my | ||||
| hosts had left over. They are really delightful. | ||||
| 
 | ||||
| Now I wanna make echo(1). This will serve as my HelloWorld as it uses stdout | ||||
| printing and, beyond the usual HelloWorld, very light argument handling. The | ||||
| book mentions cargo(1) which I will be using but for now I'll stick to single | ||||
| .rs files because echo(1) shouldn't have any dependencies. | ||||
| 
 | ||||
| It looks like std::env will give me stuff relating to arguments, std::env::args | ||||
| or std::env::args_os. According to StackOverflow the difference is in typing. | ||||
| I've heard docs.rs has some documentation but looking at the site it looks like | ||||
| it only documents third party cargo crates, which are like C libraries but (I | ||||
| think) included per-project so as to not muck up the system (I hope). I looked | ||||
| up "rust std env" and found docs.rust-lang.org which has /std/env which was | ||||
| what I needed. | ||||
| 
 | ||||
| The Rust documentation summarizes more thoroughly but, basically, an OsString, | ||||
| the type instances of which are iterated through by (oh my god this sentence is | ||||
| a prepositional mess I give up) an OsString is a fat pointer or whatever the | ||||
| Rust equivalent is while a String is probably just a nul-terminated sequence of | ||||
| bytes. Implementation-defined of course but Rust documentation notes that | ||||
| OsString should be converted to a CStr before being used in UNIX system calls. | ||||
| A nice detail I'm happy to know! I shouldn't have to do any string conversion; | ||||
| echo(1) should spit out *exactly* what it's given (opinion; implementations | ||||
| differ) just with space delimiting and newline ending. Hopefully there's a way | ||||
| for me to print out an OsString without conversion or anything. I need to `use | ||||
| std::ffi::{OsStr, OsString};` or something like that I think but I'm gonna try | ||||
| with just `use std::env;` at first. | ||||
| 
 | ||||
| The use of echo(1) is defined for argc<2 (print a newline alone; argc can be | ||||
| zero without consequence here) and argc>=2, so it won't be necessary to return | ||||
| a value from main(), Rust can just use the default successful value. | ||||
| 
 | ||||
| It looks like OsStr and OsString are from std::ffi which provides tools for FFI | ||||
| bindings. This also notes that the Rust String is also fat and not nul | ||||
| -terminated. It looks like the difference is that OsString represents an "owned | ||||
| platform string" and an OsStr represents a "borrowed reference to a platform | ||||
| string". This, I think, relates to memory management and a Borrow Checker | ||||
| (spooky) about which I haven't gotten around to learning. Rust's std::ffi is | ||||
| fascinating but while learning Rust I wanna be doing things oxidatiously or | ||||
| whatever and not doing a thin Rust wrapper and then my usual C bullshit. One of | ||||
| the things about Rust that excites me is that it seems to be able to make | ||||
| guarantees about project stability C can't but I don't know much about that | ||||
| except the stuff Mars has shown me that I don't quite understand. | ||||
| 
 | ||||
| So how do I iterate through env::args_os? According to its reference page, | ||||
| ```rs | ||||
| use std::env; | ||||
| 
 | ||||
| fn main() { | ||||
| 	for argument in env::args_os() { | ||||
| 		println!("{argument:?}"); | ||||
| 	} | ||||
| } | ||||
| ``` | ||||
| Wow! What the fuck is a println!? According to the Rust book all we need to | ||||
| know is that the `!` suffix is some Hungarian notation esque marker that | ||||
| println!() is a macro. The Rust documentation provides a definition, I think, | ||||
| of println: | ||||
| ```rs | ||||
| macro_rules! println { | ||||
| 	() => { ... }; | ||||
| 	($($arg:tt)*) => { ... }; | ||||
| } | ||||
| ``` | ||||
| I think the `{ ... }` notes abridged portions and the [...]` => { ... };` | ||||
| indicates that one case is triggered by println receiving no arguments and the | ||||
| other case is triggered by println receiving any other amount of arguments. I | ||||
| don't know if this is actual code or anything but yeah uh... Rust macros. Cool. | ||||
| What I was actually interested in is how to print without a newline. I think | ||||
| there's a macro for that too. | ||||
| ```rs | ||||
| macro_rules! print { | ||||
| 	($($arg:tt)*) => { ... }; | ||||
| } | ||||
| ``` | ||||
| Interesting. The documentation notes: | ||||
| >Prints to the standard output. | ||||
| > | ||||
| >Equivalent to the `println!` macro except that a newline is not printed at the | ||||
| >end of the message. | ||||
| >Note that stdout is frequently line-buffered by default so it may be necessary | ||||
| >to use `io::stdout().flush()` to ensure the output is emitted immediately. | ||||
| I like the note that `fflush(stdout);` is needed because this bites C beginners | ||||
| a lot when writing stuff that does something like `printf("> "); | ||||
| fgets([...]);`. | ||||
| 
 | ||||
| I see stuff in here about `.unwrap()` and `stdout().lock()` but I hope I don't | ||||
| need that because I don't understand it yet. I'm just gonna use print!. So how | ||||
| do I print! an OsString? And how do I handle argc<2? | ||||
| 
 | ||||
| The book chapter 12 actually touches on a lot of this and I stumbled upon it | ||||
| looking at std::env stuff. Here's a test I can run from the book: | ||||
| ```rs | ||||
| use std::env; | ||||
| 
 | ||||
| fn main() { | ||||
| 	let args: Vec<String> = env::args().collect(); | ||||
| 	dbg!(args); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| I'll modify that a little: | ||||
| ```rs | ||||
| use std::env; | ||||
| 
 | ||||
| fn main() { | ||||
| 	let args: Vec<OsString> = env::args().collect(); | ||||
| 	dbg!(args); | ||||
| } | ||||
| ``` | ||||
| $ rustc echo.rs | ||||
| error[E0412]: cannot find type `OsString` in this scope | ||||
|  --> echo.rs:4:19 | ||||
|   | | ||||
| 4 |     let args: Vec<OsString> = env::args().collect(); | ||||
|   |                   ^^^^^^^^ | ||||
|  --> /builddir/rust-1.73.0/library/alloc/src/string.rs:365:1 | ||||
|   | | ||||
|   = note: similarly named struct `String` defined here | ||||
|   | | ||||
| help: a struct with a similar name exists | ||||
|   | | ||||
| 4 |     let args: Vec<String> = env::args().collect(); | ||||
|   |                   ~~~~~~ | ||||
| help: consider importing this struct | ||||
|   | | ||||
| 1 + use std::ffi::OsString; | ||||
|   | | ||||
| 
 | ||||
| error: aborting due to previous error | ||||
| 
 | ||||
| For more information about this error, try `rustc --explain E0412`. | ||||
| 
 | ||||
| Okay. | ||||
| $ sed -e '1a use std::ffi::OsString' <echo.rs >echo.2.rs | ||||
| $ rustc echo.2.rs | ||||
| error[E0277]: a value of type `Vec<OsString>` cannot be built from an iterator over elements of type `String` | ||||
|  --> echo.rs:5:43 | ||||
|   | | ||||
| 5 |     let args: Vec<OsString> = env::args().collect(); | ||||
|   |                                           ^^^^^^^ value of type `Vec<OsString>` cannot be built from `std::iter::Iterator<Item=String>` | ||||
|   | | ||||
|   = help: the trait `FromIterator<String>` is not implemented for `Vec<OsString>` | ||||
|   = help: the trait `FromIterator<T>` is implemented for `Vec<T>` | ||||
| note: required by a bound in `collect` | ||||
|  --> /builddir/rust-1.73.0/library/core/src/iter/traits/iterator.rs:2049:5 | ||||
| 
 | ||||
| error: aborting due to previous error | ||||
| 
 | ||||
| For more information about this error, try `rustc --explain E0277` | ||||
| 
 | ||||
| Oh shit, I forgot to change env::args to env::os_args. | ||||
| $ sed -e '5s.args.os_args.' <echo.2.rs >echo.rs | ||||
| $ rustc echo.rs | ||||
| error[E0425]: cannot find function `os_args` in module `env` | ||||
|  --> echo.rs:5:36 | ||||
|   | | ||||
| 5 |     let args: Vec<OsString> = env::os_args().collect(); | ||||
|   |                                    ^^^^^^^ help: a function with a similar name exists: `args_os` | ||||
|  --> /builddir/rust-1.73.0/library/std/src/env.rs:793:1 | ||||
|   | | ||||
|   = note: similarly named function `args_os` defined here | ||||
| 
 | ||||
| error: aborting due to previous error | ||||
| 
 | ||||
| For more information about this error, try `rustc --explain E0425`. | ||||
| 
 | ||||
| Oops. | ||||
| $ sed -e '5s.os_args.args_os.' <echo.rs >echo.2.rs | ||||
| $ rustc echo.2.rs | ||||
| $ | ||||
| 
 | ||||
| So presumably it compiled. | ||||
| $ ./echo | ||||
| [echo.rs:6] args = [ | ||||
|     "./echo", | ||||
| ] | ||||
| 
 | ||||
| Okay, that debug macro is kinda awesome. The 500K binary makes me kinda weirded | ||||
| out, what's the size of the actual echo.c (which is the complete program) when | ||||
| compiled for arm64 (my current architecture)? | ||||
| 
 | ||||
| .rwxr-xr-x trinity trinity 9.8 KB Tue Oct 31 21:01:27 2023 🏗 a.out | ||||
| 
 | ||||
| This output is prettier than usual because I'm using lsd(1), a reimplementation | ||||
| of the standard POSIX ls(1). My girlfriend in Florida uses it and it's really | ||||
| pleasant and color codes some stuff in a way that's very useful. | ||||
| 
 | ||||
| 10K is a lot less than half a meg. I wonder if Rust is statically compiling | ||||
| versus relying on system library stuff. I don't wanna bother looking this up so | ||||
| I'll go ask Mars. | ||||
| 
 | ||||
| Its door is closed so I'll look this up. "why are rust binaries so big" popped | ||||
| up a StackOverflow post that started with "Rust uses static linking" so that | ||||
| answers my question. I would assume a statically linked C executable would be | ||||
| about that big, from memory I think this is true but don't wanna bother testing | ||||
| because I don't have the energy to look up clang arguments. | ||||
| 
 | ||||
| $ cc -static echo.c | ||||
| ld: error: unable to find library -l:libunwind.a | ||||
| ld: error: unable to find library -latomic | ||||
| ld: error: unable to find library -lc | ||||
| clang-16: error: linker command failed with exit code 1 (use -v to see invocati | ||||
| on) | ||||
| 
 | ||||
| Yeah, I'm not sorting that out, I'm not building C stuff on here to distribute. | ||||
| 
 | ||||
| I think vec.len() will tell me how many arguments I've received? | ||||
| 
 | ||||
| ```rs | ||||
| use std::env; | ||||
| use std::ffi::OsString; | ||||
| 
 | ||||
| fn main() { | ||||
| 	let args: Vec<OsString> = env::args_os().collect(); | ||||
| 	dbg!(args); | ||||
| 	dbg!(args.len()); | ||||
| } | ||||
| ``` | ||||
| $ rm echo.2.rs | ||||
| $ rustc echo.rs | ||||
| error[E0382]: borrow of moved value: `args` | ||||
|  --> echo.rs:7:10 | ||||
|   | | ||||
| 5 |     let args: Vec<OsString> = env::args_os().collect(); | ||||
|   |         ---- move occurs because `args` has type `Vec<OsString>`, which doe | ||||
| s not implement the `Copy` trait | ||||
| 6 |     dbg!(args); | ||||
|   |     ---------- value moved here | ||||
| 7 |     dbg!(args.len()); | ||||
|   |          ^^^^ value borrowed here after move | ||||
|   | | ||||
| 
 | ||||
| error: aborting due to previous error | ||||
| 
 | ||||
| For more information about this error, try `rustc --explain E0382`. | ||||
| 
 | ||||
| Okay, so now I'm talking to the borrow checker. Maybe if I assign the length to | ||||
| a variable it'll work? I don't know what I'm doing. | ||||
| 
 | ||||
| ```rs | ||||
| use std::env::args_os; | ||||
| use std::ffi::OsString; | ||||
| 
 | ||||
| fn main() { | ||||
| 	let args: Vec<OsString> = args_os().collect(); | ||||
| 	let argc = args.len(); | ||||
| 	dbg!(args); | ||||
| 	dbg!(argc); | ||||
| } | ||||
| ``` | ||||
| $ rustc echo.rs | ||||
| $ ./echo | ||||
| [echo.rs:7] args = [ | ||||
|     "./echo", | ||||
| ] | ||||
| [echo.rs:8] argc = 1 | ||||
| 
 | ||||
| Okay. I don't know why that works but it does. Something to do with memory | ||||
| management. That's not a big deal to me because I understand when I do fucky | ||||
| wucks like | ||||
| ```py | ||||
| try: print("c = " + str( | ||||
| 		(float(input("a = ")) ** 2 | ||||
| 		+ float(input("b = ")) ** 2) | ||||
| 	** 0.5)) | ||||
| except ValueError: print("input must be a number") | ||||
| except: pass | ||||
| ``` | ||||
| there's a lot of memory shit happening behind the scenes I don't have to worry | ||||
| about, unlike in the equivalent C where I would have to handle buffer overflows | ||||
| (I personally would toss the excess and skip to the newline) and string to | ||||
| float conversion. Rust requiring some steps Python wouldn't makes sense to me | ||||
| because while Rust is less pedantic it doesn't lie to me (much). | ||||
| 
 | ||||
| Let me try something now: | ||||
| ```rs | ||||
| use std::env::args_os; | ||||
| use std::ffi::OsString; | ||||
| 
 | ||||
| fn main() { | ||||
| 	let argv: Vec<OsString> = args_os.collect(); | ||||
| 	let argc = argv.len(); | ||||
| 
 | ||||
| 	if argc < 2 { | ||||
| 		println!(); | ||||
| 	} else { | ||||
| 		dbg!(argv); | ||||
| 	} | ||||
| } | ||||
| ``` | ||||
| $ rustc echo.rs | ||||
| $ ./echo | hexdump -C | ||||
| 00000000  0a                                                |.| | ||||
| 00000001 | ||||
| $ ./echo piss shit | ||||
| [echo.rs:11] argv = [ | ||||
|     "./echo", | ||||
|     "piss", | ||||
|     "shit", | ||||
| ] | ||||
| 
 | ||||
| Cool stuff. I don't think Rust has ternaries so I'm not gonna be able to do | ||||
| language tricks to make the code really compact like my C implementation: | ||||
| ```c | ||||
| #include <stdio.h> /* NULL, fprintf(3), putc(3) */ | ||||
| #include <stdlib.h> /* stdout */ | ||||
| #include <sysexits.h> /* EX_OK */ | ||||
| 
 | ||||
| int main(int argc, char **argv){ | ||||
| 
 | ||||
| 	if(*argv == NULL || *++argv == NULL){ | ||||
| 		argc = 1; | ||||
| 		putc('\n', stdout); | ||||
| 	} | ||||
| 
 | ||||
| 	while(--argc) | ||||
| 		fprintf(stdout, "%s%c", *(argv++), argc > 1 ? ' ' : '\n'); | ||||
| 
 | ||||
| 	return EX_OK; | ||||
| } | ||||
| ``` | ||||
| Something I really like is that whereas in C I note what I use from headers in | ||||
| comments like a total tool, Rust lets me bring individual structures and | ||||
| functions in so I can keep track of my dependencies in code alone. | ||||
| 
 | ||||
| I wonder if I can | ||||
| ```rs | ||||
| use std::env::args_os; | ||||
| 
 | ||||
| fn main() { | ||||
| 	let argc = args_os().collect().len(); | ||||
| 	dbg!(argc); | ||||
| } | ||||
| ``` | ||||
| $ rustc echo.rs | ||||
| error[E0282]: type annotations needed | ||||
|   --> echo.rs:5:26 | ||||
|   | | ||||
| 4 |     let argc = args_os().collect().len(); | ||||
|   |                          ^^^^^^^ cannot infer type of the type parameter `B | ||||
| ` declared on the method `collect` | ||||
|   | | ||||
| help: consider specifying the generic argument | ||||
|   | | ||||
| 4 |     let argc = args_os().collect::<Vec<_>>().len(); | ||||
|   |                                 ++++++++++ | ||||
| 
 | ||||
| error: aborting due to previous error; 1 warning emitted | ||||
| 
 | ||||
| For more information about this error, try `rustc --explain E0282`. | ||||
| 
 | ||||
| Okay, how about | ||||
| ```rs | ||||
| use std::env::args_os; | ||||
| use std::ffi::OsString; | ||||
| 
 | ||||
| fn main() { | ||||
| 	let argc = args_os().collect::Vec<OsString>().len(); | ||||
| 	dbg!(argc); | ||||
| } | ||||
| ``` | ||||
| I guess function::type() specifies the type of which function should be | ||||
| returning. That sort of makes sense? C doesn't have generic functions like that | ||||
| but I think I understand some of what's happening there. | ||||
| $ rustc echo.rs | ||||
| error: generic parameters without surrounding angle brackets | ||||
|   --> echo.rs:5:35 | ||||
|   | | ||||
| 5 |     let argc = args_os().collect::Vec<OsString>().len(); | ||||
|   |                                   ^^^^^^^^^^^^^ | ||||
|   | | ||||
| help: surround the type parameters with angle brackets | ||||
|   | | ||||
| 5 |     let argc = args_os().collect::<Vec<OsString>>().len(); | ||||
|   |                                   +             + | ||||
| 
 | ||||
| error: aborting due to previous error | ||||
| 
 | ||||
| Okay. I'm changing that without copying my code because I'm not motivated to do | ||||
| so. Also the actual errors are probably not byte-for-byte if for whatever | ||||
| reason you're following along at home (why would you? I don't know what I'm | ||||
| doing) because my code actually has a ton of snippets commented out so I don't | ||||
| need to retype everything. | ||||
| 
 | ||||
| I made the changes it suggested and the program works. Neat. But do I need that | ||||
| local variable? | ||||
| ```rs | ||||
| use std::env::args_os; | ||||
| use std::ffi::OsString; | ||||
| 
 | ||||
| fn main() { | ||||
| 	if args_os().collect::<Vec<OsString>>().len() < 2 { | ||||
| 		println!(); | ||||
| 	} else { | ||||
| 	} | ||||
| } | ||||
| ``` | ||||
| $ rustc echo.c | ||||
| $ | ||||
| 
 | ||||
| No I don't! Only if I'm using it more than once, which makes sense. I'd like to | ||||
| forego println!() though because I have a feeling this prelude-provided macro | ||||
| will do platform-specific things and differ on NT vs UNIX due to line ending | ||||
| conventions. I don't like that for a program that's supposed to follow POSIX. | ||||
| It looks like std::io::Stdout exists so I'm gonna use that and put a lock on | ||||
| std::stdout so I can write to it. I think this works? | ||||
| ```rs | ||||
| use std::env::args_os; | ||||
| use std::io::{Write, stdout}; | ||||
| use std::ffi::OsString; | ||||
| 
 | ||||
| fn main() { | ||||
| 	let mut stdout = stdout().lock(); | ||||
| 	if args_os().collect::<Vec<OsString>>().len() < 2 { | ||||
| 		stdout.write(b"\n"); // Rust wants a 'b' prefix | ||||
| 	} else { | ||||
| 	} | ||||
| } | ||||
| ``` | ||||
| $ rustc echo.rs | ||||
| warning: unused `Result` that must be used | ||||
|  --> echo.rs:8:9 | ||||
|   | | ||||
| 8 |         stdout.write(b"\n"); | ||||
|   |         ^^^^^^^^^^^^^^^^^^^ | ||||
|   | | ||||
|   = note: this `Result` may be an `Err` variant, which should be handled | ||||
|   = note: `#[warn(unused_must_use)]` on by default | ||||
| help: use `let _ = ...` to ignore the resulting value | ||||
|   | | ||||
| 8 |         let _ = stdout.write(b"\n"); | ||||
|   |         +++++++ | ||||
| 
 | ||||
| warning: 1 warning emitted | ||||
| 
 | ||||
| Okay, a note that I should handle the possibility of an error. I don't know how | ||||
| to do that so I won't, like a true in-the-field professional. | ||||
| 
 | ||||
| I guess b"\n" is a Rust byte string. I don't think it's super important just | ||||
| yet for me to know what that is so I'm gonna assume I'm fine. | ||||
| 
 | ||||
| I'm feeling devious. | ||||
| ```rs | ||||
| use std::env::args_os; | ||||
| use std::io::{Write, stdout}; | ||||
| use std::ffi::OsString; | ||||
| 
 | ||||
| fn main() { | ||||
| 	let mut stdout = stdout().lock(); | ||||
| 	if args_os().collect::<Vec<OsString>>().len() >= 2 { | ||||
| 		for argument in args_os() { | ||||
| 			stdout.write(argument); | ||||
| 			stdout.write(b" "); | ||||
| 		} | ||||
| 	} | ||||
| 	stdout.write(b"\n") | ||||
| } | ||||
| ``` | ||||
| $ rustc echo.c | ||||
| error[E0308]: mismatched types | ||||
|  --> echo.rs:9:26 | ||||
|   | | ||||
| 9 |             stdout.write(argument); | ||||
|   |                    ----- ^^^^^^^^ expected `&[u8]`, found `OsString` | ||||
|   |                    | | ||||
|   |                    arguments to this method are incorrect | ||||
|   | | ||||
| note: method defined here | ||||
|  --> /builddir/rust-1.73.0/library/std/src/io/mod.rs:1461:8 | ||||
| 
 | ||||
| error: aborting due to previous error | ||||
| 
 | ||||
| For more information about this error, try `rustc --explain E0308`. | ||||
| 
 | ||||
| So I could look up how to turn an OsString into a `&[u8]` but I need to know | ||||
| what that is because echo(1) shouldn't be dependent on "proper input" (UTF-1 | ||||
| should work as well as UTF-8). I checked the std::ffi::OsString methods but | ||||
| none of them really told me anything I think I can use so I'm gonna look at | ||||
| std::io. | ||||
| 
 | ||||
| Looking at the primitive u8, it's an 8-bit unsigned integer which should be | ||||
| fine for my uses. The method into_os_str_bytes() should work to convert | ||||
| std::ffi::OsString into a Vec<u8> but the documentation notes that this is | ||||
| "a nightly-only experimental API". Whatever, probably fine. | ||||
| 
 | ||||
| ```rs | ||||
| use std::env::args_os; | ||||
| use std::io::{Write, stdout}; | ||||
| use std::ffi::OsString; | ||||
| 
 | ||||
| fn main() { | ||||
| 	let mut stdout = stdout().lock(); | ||||
| 	if args_os().collect::<Vec<OsString>>().len() >= 2 { | ||||
| 		for argument in args_os() { | ||||
| 			stdout.write(argument.into_os_str_bytes()); | ||||
| 			stdout.write(b" "); | ||||
| 		} | ||||
| 	} | ||||
| 	stdout.write(b"\n"); | ||||
| } | ||||
| ``` | ||||
| $ rustc echo.c | ||||
| error[E0658]: use of unstable library feature 'os_str_bytes' | ||||
|  --> echo.rs:9:35 | ||||
|   | | ||||
| 9 |             stdout.write(argument.into_os_str_bytes()); | ||||
|   |                                   ^^^^^^^^^^^^^^^^^ | ||||
|   | | ||||
|   = note: see issue #111544 <https://github.com/rust-lang/rust/issues/111544> f | ||||
| or more information | ||||
| 
 | ||||
| error[E0308]: mismatched types | ||||
|  --> echo.rs:9:26 | ||||
|   | | ||||
| 9 |             stdout.write(argument.into_os_str_bytes()); | ||||
|   |                    ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&[u8]`, fou | ||||
| nd `Vec<u8>` | ||||
|   |                    | | ||||
|   |                    arguments to this method are incorrect | ||||
|   | | ||||
|   = note: expected reference `&[u8]` | ||||
|                 found struct `Vec<u8>` | ||||
| note: method defined here | ||||
|  --> /builddir/rust-1.73.0/library/std/src/io/mod.rs:1461:8 | ||||
| help: consider borrowing here | ||||
|   | | ||||
| 9 |             stdout.write(&argument.into_os_str_bytes()); | ||||
|   |                          + | ||||
| 
 | ||||
| error: aborting due to 2 previous errors | ||||
| 
 | ||||
| Some errors have detailed explanations: E0308, E0658. | ||||
| For more information about an error, try `rustc --explain E0308`. | ||||
| 
 | ||||
| Okay, I'll add that ampersand the borrow checker desires. I'm not sure how this | ||||
| works still. | ||||
| $ rustc echo.rs | ||||
| error[E0658]: use of unstable library feature 'os_str_bytes' | ||||
|  --> echo.rs:9:36 | ||||
|   | | ||||
| 9 |             stdout.write(&argument.into_os_str_bytes()); | ||||
|   |                                    ^^^^^^^^^^^^^^^^^ | ||||
|   | | ||||
|   = note: see issue #111544 <https://github.com/rust-lang/rust/issues/111544> f | ||||
| or more information | ||||
| 
 | ||||
| error: aborting due to previous error | ||||
| 
 | ||||
| For more information about this error, try `rustc --explain E0658`. | ||||
| 
 | ||||
| So how do I use an unstable library feature? I'll use the rustc facilities. | ||||
| 
 | ||||
| $ rustc --explain E0658 | ||||
| 
 | ||||
| This brought me into a manual snippet shown in my configured pager (I think) | ||||
| with instructions on how to add a feature flag. I then did what it said and | ||||
| wasn't anywhere better so I wonder if there's another way to turn an OsString | ||||
| into a &[u8]. | ||||
| 
 | ||||
| Then Mars came into the room and greeted me and I asked it how to make this | ||||
| shit work. Apparently an issue is I'm running stable rustc and in order to use | ||||
| nightly rustc stuff I need nightly rustc provided by using rustup instead of | ||||
| the packaged rust toolchain. I don't really wanna do that but I also don't | ||||
| really wanna give up so I think I'm just gonna make this a shitty echo(1) | ||||
| implementation that limits input to UTF-8. But first I wanna see how someone | ||||
| else has done this already. | ||||
| 
 | ||||
| https://github.com/uutils/coreutils.git src/uu/echo/src/echo.rs L119: | ||||
| >pub fn uumain(args: impl uucore::Args) -> UResult<()> { | ||||
| >    let args = args.collect_lossy(); | ||||
| >    let matches = uu_app().get_matches_from(args); | ||||
| > | ||||
| >    let no_newline = matches.get_flag(options::NO_NEWLINE); | ||||
| >    let escaped = matches.get_flag(options::ENABLE_BACKSLASH_ESCAPE); | ||||
| >    let values: Vec<String> = match matches.get_many::<String>(options::STRING | ||||
| ) { | ||||
| >        Some(s) => s.map(|s| s.to_string()).collect(), | ||||
| >        None => vec![String::new()], | ||||
| >    }; | ||||
| > | ||||
| >    execute(no_newline, escaped, &values) | ||||
| >        .map_err_context(|| "could not write to stdout".to_string()) | ||||
| >} | ||||
| 
 | ||||
| Those rat bastards did std::env::args.collect_lossy()! Those utter tools! I | ||||
| imagine this doesn't work for binary data but I don't know and I'm not building | ||||
| this because I don't wanna figure out how to right now. | ||||
| 
 | ||||
| Everyone is going to sleep now except me so I now feel like I need to get an | ||||
| echo(1) implementation working on this, the first day I've actually started to | ||||
| learn Rust. I'm just gonna go with std::env::args and Strings. | ||||
| 
 | ||||
| Mars also mentioned some Rust types stuff, namely &[u8] being a borrowed slice | ||||
| of u8s or something. I sort of got it and sort of didn't, I did at the time I | ||||
| just forgot. Sorry! | ||||
| 
 | ||||
| Also it came back out after I wrote that to greet me and then promptly | ||||
| disappeared. | ||||
| 
 | ||||
| This spits out a lot of warnings: | ||||
| ```rs | ||||
| use std::env::args; | ||||
| use std::io::{Write, stdout}; | ||||
| 
 | ||||
| fn main() { | ||||
| 	let mut stdout = stdout().lock(); | ||||
| 	if args().collect::<Vec<String>>().len() >= 2 { | ||||
| 		for argument in args() { | ||||
| 			stdout.write(&argument.as_bytes()); | ||||
| 			stdout.write(b" "); | ||||
| 		} | ||||
| 	} | ||||
| 	stdout.write(b"\n"); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| This is nice but print!() handles errors I think so I'm just going back to | ||||
| that. | ||||
| 
 | ||||
| ```rs | ||||
| use std::env::args; | ||||
| 
 | ||||
| fn main() { | ||||
| 	if args().collect::<Vec<String>>().len() >= 2 { | ||||
| 		for argument in args() { | ||||
| 			print!(argument); | ||||
| 			print!(" "); | ||||
| 		} | ||||
| 	} | ||||
| 	print!("\n"); | ||||
| } | ||||
| ``` | ||||
| $ rustc echo.c | ||||
| error: format argument must be a string literal | ||||
|  --> echo.rs:6:20 | ||||
|   | | ||||
| 6 |             print!(argument); | ||||
|   |                    ^^^^^^^^ | ||||
|   | | ||||
| help: you might be missing a string literal to format with | ||||
|   | | ||||
| 6 |             print!("{}", argument); | ||||
|   |                    +++++ | ||||
| 
 | ||||
| error: aborting due to previous error | ||||
| 
 | ||||
| Okay. | ||||
| 
 | ||||
| ```rs | ||||
| use std::env::args; | ||||
| 
 | ||||
| fn main() { | ||||
| 	if args().collect::<Vec<String>>().len() >= 2 { | ||||
| 		for argument in args() { | ||||
| 			print!("{}", argument); | ||||
| 			print!(" "); | ||||
| 		} | ||||
| 	} | ||||
| 	print!("\n"); | ||||
| } | ||||
| ``` | ||||
| $ rustc echo.c | ||||
| $ ./echo hello world | ||||
| ./echo hello world | ||||
| 
 | ||||
| The issue is the first argument is coming along for the ride in that for loop. | ||||
| How do I skip the first iteration of an iterator? | ||||
| 
 | ||||
| [trial and error with .rs files and rustc omitted] | ||||
| 
 | ||||
| Oh. | ||||
| 
 | ||||
| ```rs | ||||
| use std::env::args; | ||||
| 
 | ||||
| fn main() { | ||||
| 	if args().collect::<Vec<String>>().len() >= 2 { | ||||
| 		for argument in args().skip(1) { | ||||
| 			print!("{} ", argument); | ||||
| 		} | ||||
| 	} | ||||
| 	print!("\n"); | ||||
| } | ||||
| ``` | ||||
| $ rustc echo.c | ||||
| $ ./echo Hello, world! | ||||
| Hello, world! | ||||
| $ ./echo Happy Halloween! | ||||
| Happy Halloween! | ||||
| 
 | ||||
| That's where I'm leaving my Rust education today. And this is day 1. Pretty | ||||
| good! | ||||
| 
 | ||||
| 
 | ||||
| /blah/2023-10-29.html | ||||
| 
 | ||||
| Another journal, in its entirety | ||||
|  | ||||
							
								
								
									
										5
									
								
								true/true.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								true/true.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| use std::process::ExitCode; | ||||
| 
 | ||||
| fn main() -> ExitCode { | ||||
|     ExitCode::from(0) | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user