In the spirit of Software Tools, the aim is to make components re-usable in three ways:
Result
type, so the caller
can decide how to deal with the error.A fourth avenue may be explored, which is to adopt the nushell approach to transferring tabular data between commands.
For a related project that also follows Software Tools in Rust, and may serve as an interesting comparison, see Sweater. A more feature-rich project is uutils coreutils, which as the name suggests, is a Rust implementation analogous to GNU Coreutils.
Higher-order-functions (HOFs) are frequently used to reduce code
complexity, verbosity, and the risk of errors. Primary examples are
map
, for_each
(like map
but effectful), and fold
. As pointed
out in Software Tools, pp 21, "The best programs are designed in
terms of loosely coupled functions that each does a simple task."
Some other references that refelect functional programming values:
- page 36, a discussion on break
: the suggestions also coincide largley
with recursive functions.
- pages 44-45 discuss defensive programming by guarding control variables
with safety checks. In functional programming, such control variables
often do not appear, so safety checks are unnecessary due to the usage
of HOFs being safe by design. Page 45 also points out that non-voluminous
code listings are easier to debug (which I agree with, and a functional
style typically enables this), though we also want to warn against making
code overly terse. Experience is the best guide in this case.
cp
wc
detab
entab
echo
Since the goal is to make the software both as self-contained and as illustrative as possible, we've tried to rely on very few dependencies. The following exceptions exist:
peeking_take_while
function for
Peekable
iterators. This behaves more of how would would expect for
a take_while
function compared to the standard take_while
implementation,
which will "lose" the first element after a take_while
streak ends.std
's mutable structures for large
data types, and while Rust makes mutation far safer than most languages,
mutation can still result in confusion at times, so in the cases where
clarity is more important than performance (or performance doesn't
matter much, e.g. one-ops), it may be preferable to use immutable data
structures.Using todo!()
from std::todo
is a helpful way to incrementally
develop a feature while still getting feedback from the
compiler. [TODO: show example]
A caveat is that
currently you need code in the function after the todo!()
, even
if it doesn't match the type. For instance, we can use a function
like:
rust
pub fn some_num() -> i32 {
todo!(); ();
}
Most beneficial is that rustc
will warn you if you a todo!()
is
left in your code, since it would result in a panic if that execution
path were to occur.
```plain nix-shell -p rustup cargo
```
Currently, to generate small builds the following commands are required.
plain
rustup component add rust-src --toolchain nightly
2.
plain
cargo +nightly build -Z build-std --target x86_64-unknown-linux-gnu --release
strip
binary - see links in notesWe use cargo-husky to keep in
line; it enforces several checks with a pre-push
hook. Sometimes it
is a bit restrictive, so if we need to push
in-progress work to a branch, we can use
git push --no-verify -u origin feature_branch
.
Cargo-husky expects certain files to be at the root of the repository,
thus the symlinks.
We include the following, less stringent checks for pre-commit.
```bash
#
for FILE in git diff --cached --name-only
; do
if [[ -f "$FILE" ]] && [[ $FILE == *.rs ]] \
&& ! rustup run nightly rustfmt --unstable-features \
--skip-children $FILE; then
echo "Commit rejected due to invalid formatting of \"$FILE\" file."
exit 1
fi
done
cd Rust/sfw-tools && cargo readme > README.md && git add README.md
``
As can be seen this also generates the README from doc comments in
lib.rs`.
License: MPL-2.0