rust-ownership - 解答
実装コード
所有権関連関数
// src/ownership.rs
/// 所有権を受け取り、変換して返す
pub fn process_owned(mut data: String) -> String {
data.make_ascii_uppercase();
data.push_str("_PROCESSED");
data
}
/// 所有権を消費して新しい値を生成
pub fn consume_and_create(a: String, b: String) -> String {
format!("{}-{}", a, b)
}
/// 条件に基づいて所有権を返すか消費
pub fn conditional_ownership(data: String, keep: bool) -> Option<String> {
if keep {
Some(data)
} else {
// data は drop される
None
}
}
借用関連関数
// src/borrowing.rs
/// 不変借用で読み取り
pub fn process_borrowed(data: &str) -> String {
data.to_uppercase()
}
/// 不変借用で検索
pub fn find_substring(haystack: &str, needle: &str) -> Option<usize> {
haystack.find(needle)
}
/// 複数の不変借用
pub fn compare_strings(a: &str, b: &str) -> std::cmp::Ordering {
a.len().cmp(&b.len())
}
/// 可変借用で変更
pub fn modify_in_place(data: &mut String) {
*data = data.to_uppercase();
data.push_str("!");
}
/// 可変借用でフィルタリング
pub fn retain_alphabetic(data: &mut String) {
data.retain(|c| c.is_alphabetic());
}
/// スライスを返す(借用を返す)
pub fn first_word(s: &str) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
ライフタイム構造体
// src/lifetime.rs
/// 参照を保持する構造体
pub struct TextAnalyzer<'a> {
text: &'a str,
}
impl<'a> TextAnalyzer<'a> {
pub fn new(text: &'a str) -> Self {
TextAnalyzer { text }
}
pub fn word_count(&self) -> usize {
self.text.split_whitespace().count()
}
pub fn char_count(&self) -> usize {
self.text.chars().count()
}
/// テキスト内のパターンを検索し、マッチした部分を返す
pub fn find_pattern(&self, pattern: &str) -> Vec<&'a str> {
self.text
.match_indices(pattern)
.map(|(i, _)| &self.text[i..i + pattern.len()])
.collect()
}
/// 行のイテレータを返す
pub fn lines(&self) -> impl Iterator<Item = &'a str> {
self.text.lines()
}
}
/// 複数ライフタイムを持つ構造体
pub struct Merger<'a, 'b> {
left: &'a str,
right: &'b str,
}
impl<'a, 'b> Merger<'a, 'b> {
pub fn new(left: &'a str, right: &'b str) -> Self {
Merger { left, right }
}
/// 短い方のライフタイムを持つ結果を返す
pub fn merge(&self) -> String {
format!("{}{}", self.left, self.right)
}
/// 左側の参照を返す('a ライフタイム)
pub fn left(&self) -> &'a str {
self.left
}
/// 右側の参照を返す('b ライフタイム)
pub fn right(&self) -> &'b str {
self.right
}
}
/// 入力と同じライフタイムの参照を返す
pub fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
/// 静的ライフタイムの参照を返す
pub fn static_str() -> &'static str {
"This string lives forever"
}
ボーナス: カスタムイテレータ
/// 単語を返すイテレータ
pub struct Words<'a> {
text: &'a str,
position: usize,
}
impl<'a> Words<'a> {
pub fn new(text: &'a str) -> Self {
Words { text, position: 0 }
}
}
impl<'a> Iterator for Words<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
// 空白をスキップ
while self.position < self.text.len()
&& self.text.as_bytes()[self.position].is_ascii_whitespace()
{
self.position += 1;
}
if self.position >= self.text.len() {
return None;
}
let start = self.position;
// 単語の終わりを探す
while self.position < self.text.len()
&& !self.text.as_bytes()[self.position].is_ascii_whitespace()
{
self.position += 1;
}
Some(&self.text[start..self.position])
}
}
テストコード
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_process_owned() {
let s = String::from("hello");
let result = process_owned(s);
assert_eq!(result, "HELLO_PROCESSED");
}
#[test]
fn test_process_borrowed() {
let s = String::from("hello");
let result = process_borrowed(&s);
assert_eq!(result, "HELLO");
assert_eq!(s, "hello"); // 元の値は変更されていない
}
#[test]
fn test_modify_in_place() {
let mut s = String::from("hello");
modify_in_place(&mut s);
assert_eq!(s, "HELLO!");
}
#[test]
fn test_text_analyzer() {
let text = String::from("hello world");
let analyzer = TextAnalyzer::new(&text);
assert_eq!(analyzer.word_count(), 2);
assert_eq!(analyzer.char_count(), 11);
}
#[test]
fn test_merger() {
let left = String::from("Hello, ");
let right = String::from("World!");
let merger = Merger::new(&left, &right);
assert_eq!(merger.merge(), "Hello, World!");
}
#[test]
fn test_longest() {
let s1 = String::from("short");
let s2 = String::from("much longer");
let result = longest(&s1, &s2);
assert_eq!(result, "much longer");
}
#[test]
fn test_words_iterator() {
let text = "hello world rust";
let words: Vec<_> = Words::new(text).collect();
assert_eq!(words, vec!["hello", "world", "rust"]);
}
}