From 370b7c4526af859fdca7ea0ddf2a339109473734 Mon Sep 17 00:00:00 2001 From: Stanley Kudrow Date: Fri, 26 Jun 2026 02:15:06 +0300 Subject: [PATCH 1/2] init libft/rust --- .gitignore | 29 ++++++++++++++++++++++++++ README.md | 43 ++++++++++++++++++++++++++++++--------- rust/Cargo.lock | 7 +++++++ rust/Cargo.toml | 6 ++++++ rust/src/ft_chr.rs | 7 +++++++ rust/src/ft_str.rs | 14 +++++++++++++ rust/src/lib.rs | 9 ++++++++ rust/tests/test_ft_chr.rs | 10 +++++++++ rust/tests/test_ft_str.rs | 17 ++++++++++++++++ 9 files changed, 132 insertions(+), 10 deletions(-) create mode 100644 rust/Cargo.lock create mode 100644 rust/Cargo.toml create mode 100644 rust/src/ft_chr.rs create mode 100644 rust/src/ft_str.rs create mode 100644 rust/src/lib.rs create mode 100644 rust/tests/test_ft_chr.rs create mode 100644 rust/tests/test_ft_str.rs diff --git a/.gitignore b/.gitignore index 2ebb751..f11ce61 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +### https://github.com/github/gitignore/blob/main/C.gitignore + # Prerequisites *.d @@ -56,3 +58,30 @@ dkms.conf # https://testanything.org/tap-specification *.tap !c/test_report.tap + +### https://github.com/github/gitignore/blob/main/Rust.gitignore + +# Generated by Cargo +# will have compiled files and executables +debug +target + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# Generated by cargo mutants +# Contains mutation testing data +**/mutants.out*/ + +# rustc will dump stack traces when hitting an internal compiler error to PWD +rustc-ice-*.txt + +# RustRover +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +.idea/ diff --git a/README.md b/README.md index bedd3b6..bc07695 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,17 @@ The first project of the School 42. -Linted according to the `norminette` v3.3.51 rules (at the time of writing which was a long time ago) and **not anymore**. +Linted according to the `norminette` v3.3.51 rules (at the time of writing which was a long time ago)... **not anymore**. ## Table of Contents - [Overview](#overview) - [Libft in C](#libft-in-c) +- [Libft in Rust](#libft-in-rust) - [References](#references) + - [C](#c) + - [Rust](#rust) + - [C++](#c++) ### Overview @@ -18,29 +22,48 @@ This project was done in C during my learning at the School 42 (School 21). Now I reorganised the original code and placed it in the [c](./c) directory. To build the project, use `make` or `make bonus` which will create a static "libft.a" library. During my time at School 42, I checked the correctness of the library using the following testing programs (**attention**, these programs are written by people, so rely on them cautiously): -- [libft-unit-test](https://github.com/alelievr/libft-unit-test) (contributed more long time ago) -- [libftTester](https://github.com/Tripouille/libftTester) (contributed less and also long time ago) +- [libft-unit-test](https://github.com/alelievr/libft-unit-test) (contributed a while back) +- [libftTester](https://github.com/Tripouille/libftTester) (contributed some time ago) - [libft-war-machine](https://github.com/ska42/libft-war-machine) -It is normal (!) if some of your functions are reported as not protected, you don't need to be scared of opinionated "boom" messages..or maybe you have to depending on the School 42 current subject. +It is normal (!) if some of your functions are flagged as unprotected, you don't need to be scared of opinionated "boom" messages (or maybe you have to according to the School 42 latest subject). -Later on, I decided that I needed (for some unexplicable reason) to learn how to write unit tests in C and this project seems to me like a perfect candidate to start with. I picked [Check](https://libcheck.github.io/check/) framework because it is: +Later on, I got into my head that that I need (for some inexplicable reason) to learn how to write unit tests in C and this project seems to me like the perfect place to start. I picked [Check](https://libcheck.github.io/check/) framework because it is: - installable via `sudo apt install check` (see the [Installing Check](https://libcheck.github.io/check/web/install.html) section for more details); - simple enough and has some documentation and even tutorials: [basic](https://libcheck.github.io/check/doc/check_html/check_3.html) and [advanced](https://libcheck.github.io/check/doc/check_html/check_4.html#Advanced-Features) + not so-long [API reference](https://libcheck.github.io/check/doc/doxygen/html/check_8h.html); -- advised/required (?) at School 21. +- recommended or even required (?) at School 21. -So, if you are a student or an "intern" at School 21 or 42, you might want to use the "Check" framework in your C projects. Tests are available in the [c/tests](./c/tests) directory. Tests are not organised according to best practices, so please don't expect them to be idiomatic and well-structured. **Disclaimer**: I had and have no eagerness to provide full coverage for this project in C, only functions or data structures of interest that was enough **for me** to master "Check" to some basic level. +So, if you are a student or an "intern" at School 21 or 42, you might want to use the "Check" framework in your C projects. Tests are available in the [c/tests](./c/tests) directory. The tests are not written following best practices, please don’t expect idiomatic or well‑structured code. **Disclaimer**: I had and have no eagerness to provide full coverage for this project in C, only functions or data structures of interest that was enough **for me** to master "Check" to some basic level. + +### Libft in Rust + +Libft is a library, and I wanted to learn how to build a proper Rust crate—something I could publish and reuse in other projects. This seemed like a good first challenge. + +- `cargo new --lib %my-fancy-lib%` - creates a library project/directory with ***%my-fancy-lib%*** name; +- `cargo fmt` - format the code; +- `cargo clippy` - built-in linter; +- `cargo build` - build/compile the project; +- `cargo test` - run tests. ### References -Some of my other 42cursus projects in C: +#### C +Some of my other 42cursus projects in C: - [get-next-line](https://github.com/stankudrow/get_next_line) - [ft_printf](https://github.com/stankudrow/ft_printf) Resources on C programming language: - - [CodeVault playlist](https://www.youtube.com/@CodeVault/playlists) - must watch, practically all videos. -The reason why I won't learn C++ and practice it on the libft -> [have fun](https://www.youtube.com/watch?v=7fGB-hjc2Gc). +#### Rust + +- [The Cargo Book][cargo_book_ref] - Cargo is the Rust package manager: dependencies, package compilation, building, uploading to [crates.io package registry](https://crates.io/) etc. + +#### C++ + +No way, thanx. The reason why I won't learn C++ and practice it on the libft -> have fun (or any other emotions) watching [this video](https://www.youtube.com/watch?v=7fGB-hjc2Gc). + + +[cargo_book_ref]: https://doc.rust-lang.org/cargo/index.html diff --git a/rust/Cargo.lock b/rust/Cargo.lock new file mode 100644 index 0000000..e4bdfe5 --- /dev/null +++ b/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "libft" +version = "0.1.0" diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 0000000..cbac016 --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "libft" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/rust/src/ft_chr.rs b/rust/src/ft_chr.rs new file mode 100644 index 0000000..7748276 --- /dev/null +++ b/rust/src/ft_chr.rs @@ -0,0 +1,7 @@ +// If my goal had been to reproduce the "original" ft_isascii function, +// its signature would have been `pub fn ft_isascii(c: i32) -> i32`, +// but I don't chase that goal +pub fn ft_isascii(c: u8) -> bool { + // `c >= 0` is redundant when c is unsigned + c <= 127 +} diff --git a/rust/src/ft_str.rs b/rust/src/ft_str.rs new file mode 100644 index 0000000..95c8b70 --- /dev/null +++ b/rust/src/ft_str.rs @@ -0,0 +1,14 @@ +// `pub` = public -> is visible from outside this module. +// Without pub this function would have been private. +pub fn ft_strlen(s: &str) -> usize { + // &str in Rust is UTF-8 + // For ASCII string, `s.len() == s.chars().count()` + // C-strlen is about chars as bytes, that is why `s.as_bytes()` + + // return s.len(); // return is possible, but redundant + s.len() // no semicolon here!!! + + // In a function, the last expression is returned automatically + // If the last expression is followed by a semicolon, + // then it becomes a statement/instruction that returns nothing (semicolon silences). +} diff --git a/rust/src/lib.rs b/rust/src/lib.rs new file mode 100644 index 0000000..d93fd45 --- /dev/null +++ b/rust/src/lib.rs @@ -0,0 +1,9 @@ +// `pub mod XXX;` +// `mod`ule (file) XXX is claimed to exist. +// `pub` makes this module public, i.e. visible from the outside. + +pub mod ft_chr; +pub mod ft_str; + +pub use ft_chr::ft_isascii; +pub use ft_str::ft_strlen; diff --git a/rust/tests/test_ft_chr.rs b/rust/tests/test_ft_chr.rs new file mode 100644 index 0000000..ab361ac --- /dev/null +++ b/rust/tests/test_ft_chr.rs @@ -0,0 +1,10 @@ +use libft::ft_isascii; + +#[test] +fn test_isascii() { + assert_eq!(ft_isascii(0), true); + assert_eq!(ft_isascii(100), true); + assert_eq!(ft_isascii(127), true); + assert_eq!(ft_isascii(128), false); + assert_eq!(ft_isascii(255), false); +} diff --git a/rust/tests/test_ft_str.rs b/rust/tests/test_ft_str.rs new file mode 100644 index 0000000..8073f5d --- /dev/null +++ b/rust/tests/test_ft_str.rs @@ -0,0 +1,17 @@ +use libft::ft_strlen; + +#[test] +fn test_empty() { + assert_eq!(ft_strlen(""), 0); +} + +#[test] +fn test_ascii_only() { + assert_eq!(ft_strlen("hello"), 5); +} + +#[test] +fn test_utf8_byte_length() { + let s = "Привет"; + assert!(ft_strlen(s) > s.chars().count()); +} From c85d44fac053dcbcb087e26880256b1658a3395f Mon Sep 17 00:00:00 2001 From: Stanley Kudrow Date: Sat, 27 Jun 2026 00:48:16 +0300 Subject: [PATCH 2/2] finish ft_ch(a)r.rs functions --- rust/src/ft_chr.rs | 58 ++++++++++++++++++++++++++++++-- rust/src/lib.rs | 10 ++++-- rust/tests/test_ft_chr.rs | 70 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 133 insertions(+), 5 deletions(-) diff --git a/rust/src/ft_chr.rs b/rust/src/ft_chr.rs index 7748276..bebe61e 100644 --- a/rust/src/ft_chr.rs +++ b/rust/src/ft_chr.rs @@ -1,7 +1,61 @@ // If my goal had been to reproduce the "original" ft_isascii function, // its signature would have been `pub fn ft_isascii(c: i32) -> i32`, -// but I don't chase that goal pub fn ft_isascii(c: u8) -> bool { // `c >= 0` is redundant when c is unsigned - c <= 127 + c <= 127 // the result of the last expression is returned automatically + // however, `return c <= 127;` is doable, yet not idiomatic +} + +// A function without `pub` is private -> like `static` in C +fn ft_islower(c: u8) -> bool { + // c >= b'a' && c <= b'z' // possible, but `cargo clippy` suggests... + // (b'a'..=b'z').contains(&c) // yet this also does not please clippy, so... + c.is_ascii_lowercase() // and this makes clippy happy +} + +// The following macro will suppress the complain from `cargo clippy` +#[allow(clippy::char_lit_as_u8)] +fn ft_isupper(c: u8) -> bool { + // explicit type casting via `obj as %type%` + // because implicit casting is not ppresumed and for good reason + c >= ('A' as u8) && c <= ('Z' as u8) +} + +// No need to declare/define the underlying function before this one +pub fn ft_isalnum(c: u8) -> bool { + ft_isalpha(c) || ft_isdigit(c) +} + +pub fn ft_isalpha(c: u8) -> bool { + ft_islower(c) || ft_isupper(c) +} + +// several "warnings" are silenced in one macro +#[allow(clippy::char_lit_as_u8, clippy::needless_return)] +pub fn ft_isdigit(c: u8) -> bool { + // b'0' is a byte literal, while '0' is char (Unicode) + // '0' is char, c is u8 -> incompatible types, + // either byte literal will do or explicit type casting + return c >= b'0' && c <= ('9' as u8); +} + +#[allow(clippy::needless_return)] +pub fn ft_isprint(c: u8) -> bool { + return c > 31 && c < 127; // not disallowed, why not `return` redundantly +} + +pub fn ft_tolower(c: u8) -> u8 { + if ft_isupper(c) { + return c + 32; // here return is necessary + // because the expression above is not the very last + } + c // here the `return` statement can be omitted +} + +#[allow(clippy::needless_return)] +pub fn ft_toupper(c: u8) -> u8 { + if ft_islower(c) { + return c - 32; // parentheses are unncecessary + } + return c; } diff --git a/rust/src/lib.rs b/rust/src/lib.rs index d93fd45..fe0cd7b 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1,9 +1,15 @@ // `pub mod XXX;` // `mod`ule (file) XXX is claimed to exist. // `pub` makes this module public, i.e. visible from the outside. - pub mod ft_chr; -pub mod ft_str; +pub use ft_chr::ft_isalnum; +pub use ft_chr::ft_isalpha; pub use ft_chr::ft_isascii; +pub use ft_chr::ft_isdigit; +pub use ft_chr::ft_isprint; +pub use ft_chr::ft_tolower; +pub use ft_chr::ft_toupper; + +pub mod ft_str; pub use ft_str::ft_strlen; diff --git a/rust/tests/test_ft_chr.rs b/rust/tests/test_ft_chr.rs index ab361ac..9c72486 100644 --- a/rust/tests/test_ft_chr.rs +++ b/rust/tests/test_ft_chr.rs @@ -1,10 +1,78 @@ +use libft::ft_isalnum; +use libft::ft_isalpha; use libft::ft_isascii; +use libft::ft_isdigit; +use libft::ft_isprint; +use libft::ft_tolower; +use libft::ft_toupper; #[test] -fn test_isascii() { +fn test_ft_isalnum() { + assert_eq!(ft_isalnum(0), false); + assert_eq!(ft_isalnum(b'0'), true); + assert_eq!(ft_isalnum(b'o'), true); + assert_eq!(ft_isalnum(b'9'), true); + assert_eq!(ft_isalnum(b'A'), true); + assert_eq!(ft_isalnum(b'Z'), true); + assert_eq!(ft_isalnum(b'a'), true); + assert_eq!(ft_isalnum(b'z'), true); + assert_eq!(ft_isalnum(128), false); + assert_eq!(ft_isalnum(255), false); +} + +#[test] +fn test_ft_isalpha() { + assert_eq!(ft_isalpha(0), false); + assert_eq!(ft_isalpha(b'A'), true); + assert_eq!(ft_isalpha(b'Z'), true); + assert_eq!(ft_isalpha(b'a'), true); + assert_eq!(ft_isalpha(b'z'), true); + assert_eq!(ft_isalpha(255), false); +} + +#[test] +fn test_ft_isascii() { assert_eq!(ft_isascii(0), true); assert_eq!(ft_isascii(100), true); assert_eq!(ft_isascii(127), true); assert_eq!(ft_isascii(128), false); assert_eq!(ft_isascii(255), false); } + +#[test] +fn test_ft_isdigit() { + assert_eq!(ft_isdigit(0), false); + assert_eq!(ft_isdigit(b'0'), true); + assert_eq!(ft_isdigit(b'9'), true); + assert_eq!(ft_isdigit(128), false); + assert_eq!(ft_isdigit(255), false); +} + +#[test] +fn test_ft_isprint() { + assert_eq!(ft_isprint(31), false); + assert_eq!(ft_isprint(32), true); + assert_eq!(ft_isprint(100), true); + assert_eq!(ft_isprint(126), true); + assert_eq!(ft_isprint(127), false); +} + +#[test] +fn test_ft_tolower() { + assert_eq!(ft_tolower(b'A' - 1), 64); + assert_eq!(ft_tolower(b'A'), b'a'); + assert_eq!(ft_tolower(b'Z'), b'z'); + assert_eq!(ft_tolower(b'a'), b'a'); + assert_eq!(ft_tolower(b'z'), b'z'); + assert_eq!(ft_tolower(b'z' + 1), 123); +} + +#[test] +fn test_ft_toupper() { + assert_eq!(ft_toupper(b'A' - 1), 64); + assert_eq!(ft_toupper(b'A'), b'A'); + assert_eq!(ft_toupper(b'Z'), b'Z'); + assert_eq!(ft_toupper(b'a'), b'A'); + assert_eq!(ft_toupper(b'z'), b'Z'); + assert_eq!(ft_toupper(b'z' + 1), 123); +}