THE WRITER MUST EAT -> patreon.com/trn1ty <-

blah!

ideas with no tangibility;
ideas with irrelevant supports;
ideas without value;
ideas' witlessness;
ideas' witnesses;
ideas-

<^>

2023-10-31

: 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<OsStri
ng>` 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!

<^>

No rights reserved, all rights exercised, rights turned to lefts, left in this
corner of the web.