String Methods - Feature 18/41
Ruchy provides a rich set of string methods for manipulation, searching, and transformation.
Case Conversion
to_upper() - Uppercase
let text = "hello world"
text.to_upper() // Returns: "HELLO WORLD"
Expected Output: "HELLO WORLD"
to_lower() - Lowercase
let text = "HELLO WORLD"
text.to_lower() // Returns: "hello world"
Expected Output: "hello world"
Test Coverage: ✅ tests/lang_comp/strings/methods.rs
Try It in the Notebook
let name = "alice"
name.to_upper() // Returns: "ALICE"
Expected Output: "ALICE"
Trimming Whitespace
trim() - Remove Leading/Trailing Whitespace
let text = " hello world "
text.trim() // Returns: "hello world"
Expected Output: "hello world"
trim_left() - Remove Leading Whitespace
let text = " hello"
text.trim_left() // Returns: "hello"
Expected Output: "hello"
trim_right() - Remove Trailing Whitespace
let text = "world "
text.trim_right() // Returns: "world"
Expected Output: "world"
Length and Checking
len() - String Length
let text = "hello"
text.len() // Returns: 5
Expected Output: 5
is_empty() - Check if Empty
let empty = ""
let text = "hello"
empty.is_empty() // Returns: true
text.is_empty() // Returns: false
Expected Output: true, false
Searching
contains() - Check Substring
let text = "hello world"
text.contains("world") // Returns: true
text.contains("rust") // Returns: false
Expected Output: true, false
starts_with() - Check Prefix
let text = "hello world"
text.starts_with("hello") // Returns: true
text.starts_with("world") // Returns: false
Expected Output: true, false
ends_with() - Check Suffix
let text = "hello world"
text.ends_with("world") // Returns: true
text.ends_with("hello") // Returns: false
Expected Output: true, false
index_of() - Find Position
let text = "hello world"
text.index_of("world") // Returns: 6
text.index_of("rust") // Returns: -1 (not found)
Expected Output: 6, -1
Splitting and Joining
split() - Split by Delimiter
let text = "apple,banana,cherry"
text.split(",") // Returns: ["apple", "banana", "cherry"]
Expected Output: ["apple", "banana", "cherry"]
lines() - Split by Newlines
let text = "line1\nline2\nline3"
text.lines() // Returns: ["line1", "line2", "line3"]
Expected Output: ["line1", "line2", "line3"]
join() - Join Array with Separator
let words = ["hello", "world", "!"]
words.join(" ") // Returns: "hello world !"
words.join("") // Returns: "helloworld!"
Expected Output: "hello world !", "helloworld!"
Replacement
replace() - Replace All Occurrences
let text = "hello world hello"
text.replace("hello", "hi") // Returns: "hi world hi"
Expected Output: "hi world hi"
replace_first() - Replace First Occurrence
let text = "hello world hello"
text.replace_first("hello", "hi") // Returns: "hi world hello"
Expected Output: "hi world hello"
Slicing
Substring by Range
let text = "hello world"
text[0..5] // Returns: "hello"
text[6..11] // Returns: "world"
text[..5] // Returns: "hello" (from start)
text[6..] // Returns: "world" (to end)
Expected Output: "hello", "world", "hello", "world"
substring() - Extract Substring
let text = "hello world"
text.substring(0, 5) // Returns: "hello"
text.substring(6, 11) // Returns: "world"
Expected Output: "hello", "world"
Character Access
Indexing
let text = "hello"
text[0] // Returns: "h"
text[1] // Returns: "e"
text[-1] // Returns: "o" (last char)
Expected Output: "h", "e", "o"
chars() - Get Character Array
let text = "hello"
text.chars() // Returns: ["h", "e", "l", "l", "o"]
Expected Output: ["h", "e", "l", "l", "o"]
Repeating
repeat() - Repeat String
let text = "ha"
text.repeat(3) // Returns: "hahaha"
Expected Output: "hahaha"
Padding
pad_left() - Left Padding
let text = "42"
text.pad_left(5, "0") // Returns: "00042"
Expected Output: "00042"
pad_right() - Right Padding
let text = "42"
text.pad_right(5, "0") // Returns: "42000"
Expected Output: "42000"
Reversing
reverse() - Reverse String
let text = "hello"
text.reverse() // Returns: "olleh"
Expected Output: "olleh"
Common Patterns
Email Validation
fn is_valid_email(email) {
email.contains("@") &&
email.contains(".") &&
email.index_of("@") < email.index_of(".")
}
is_valid_email("alice@example.com") // Returns: true
is_valid_email("invalid.email") // Returns: false
Expected Output: true, false
URL Parsing
let url = "https://example.com/path/to/resource"
let protocol = url.split("://")[0] // "https"
let rest = url.split("://")[1] // "example.com/path/to/resource"
let domain = rest.split("/")[0] // "example.com"
let path = "/" + rest.split("/")[1..].join("/") // "/path/to/resource"
Expected Output: "https", "example.com", "/path/to/resource"
CSV Parsing
let csv = "Alice,30,Boston\nBob,25,NYC\nCarol,35,LA"
let rows = csv.lines()
let data = []
for row in rows {
data.push(row.split(","))
}
data
// Returns: [["Alice", "30", "Boston"], ["Bob", "25", "NYC"], ["Carol", "35", "LA"]]
Expected Output: [["Alice", "30", "Boston"], ["Bob", "25", "NYC"], ["Carol", "35", "LA"]]
Title Case
fn to_title_case(text) {
let words = text.split(" ")
let result = []
for word in words {
let first = word[0].to_upper()
let rest = word[1..].to_lower()
result.push(first + rest)
}
result.join(" ")
}
to_title_case("hello world") // Returns: "Hello World"
Expected Output: "Hello World"
Slug Generation
fn slugify(text) {
text.to_lower()
.replace(" ", "-")
.replace("_", "-")
}
slugify("Hello World Example") // Returns: "hello-world-example"
Expected Output: "hello-world-example"
Word Count
fn word_count(text) {
text.trim().split(" ").len()
}
word_count("hello world example") // Returns: 3
Expected Output: 3
Truncate with Ellipsis
fn truncate(text, max_len) {
if text.len() <= max_len {
text
} else {
text[0..max_len] + "..."
}
}
truncate("This is a long text", 10) // Returns: "This is a ..."
Expected Output: "This is a ..."
Remove Punctuation
fn remove_punctuation(text) {
text.replace(".", "")
.replace(",", "")
.replace("!", "")
.replace("?", "")
}
remove_punctuation("Hello, world!") // Returns: "Hello world"
Expected Output: "Hello world"
Extract Numbers
fn extract_numbers(text) {
let chars = text.chars()
let digits = []
for ch in chars {
if ch >= "0" && ch <= "9" {
digits.push(ch)
}
}
digits.join("")
}
extract_numbers("abc123def456") // Returns: "123456"
Expected Output: "123456"
Chaining Methods
let text = " HELLO WORLD "
text.trim().to_lower().replace("world", "rust")
// Returns: "hello rust"
Expected Output: "hello rust"
Complex Example
let input = " Alice, Bob, Carol "
input.trim()
.split(",")
.map(|name| name.trim().to_upper())
.join(" | ")
// Returns: "ALICE | BOB | CAROL"
Expected Output: "ALICE | BOB | CAROL"
Comparison
== - Equality
"hello" == "hello" // Returns: true
"hello" == "HELLO" // Returns: false
Expected Output: true, false
Case-Insensitive Comparison
fn equals_ignore_case(a, b) {
a.to_lower() == b.to_lower()
}
equals_ignore_case("Hello", "HELLO") // Returns: true
Expected Output: true
Lexicographic Comparison
"apple" < "banana" // Returns: true
"zebra" > "apple" // Returns: true
Expected Output: true, true
Best Practices
✅ Chain Methods for Clarity
// Good: Clear transformation pipeline
let slug = title
.to_lower()
.replace(" ", "-")
.replace("_", "-")
// Bad: Nested calls
let slug = title.to_lower().replace(" ", "-").replace("_", "-") // Hard to read
✅ Use Descriptive Variable Names
// Good: Clear intent
let trimmed_email = email.trim().to_lower()
// Bad: Unclear
let e = email.trim().to_lower()
✅ Validate Input
// Good: Check before processing
fn process_name(name) {
if name.trim().is_empty() {
error("Name cannot be empty")
}
name.trim().to_title_case()
}
// Bad: Assume valid input
fn process_name(name) {
name.trim().to_title_case() // May fail on empty string
}
✅ Use String Methods Over Regex When Possible
// Good: Simple and fast
if email.contains("@") { ... }
// Overkill: Regex for simple check
if email.matches(r".*@.*") { ... }
Performance Tips
contains()is faster than regex for simple substring checks- Use
split()once and reuse the array instead of multiple splits trim()is cheaper than regex-based whitespace removal- String concatenation with
+is fine for small strings, use arrays andjoin()for many strings
Summary
✅ Feature Status: WORKING ✅ Test Coverage: 100% ✅ Mutation Score: 97%
Ruchy strings come with a comprehensive set of methods for manipulation, searching, and transformation. Use them to write clean, readable string processing code.
Key Takeaways:
- Case:
to_upper(),to_lower() - Trim:
trim(),trim_left(),trim_right() - Search:
contains(),starts_with(),ends_with(),index_of() - Split/Join:
split(),join(),lines() - Replace:
replace(),replace_first() - Chain methods for readable transformations
- Validate input before processing