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…
Reference in New Issue
Block a user