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>
|
</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
|
/blah/2023-10-29.html
|
||||||
|
|
||||||
Another journal, in its entirety
|
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…
Reference in New Issue
Block a user