// 105: Lifetime Basics
// Lifetime annotations tell the compiler how long references live
// Approach 1: Lifetime in function signature
// 'a means: the returned reference lives as long as the inputs
fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str {
if s1.len() >= s2.len() { s1 } else { s2 }
}
fn first_element<'a>(v: &'a [i32]) -> Option<&'a i32> {
v.first()
}
// Approach 2: Lifetime in struct
struct Important<'a> {
content: &'a str,
}
impl<'a> Important<'a> {
fn new(content: &'a str) -> Self {
Important { content }
}
fn content(&self) -> &str {
self.content
}
}
// Approach 3: Multiple lifetimes
fn first_word<'a>(s: &'a str) -> &'a str {
let bytes = s.as_bytes();
for (i, &byte) in bytes.iter().enumerate() {
if byte == b' ' { return &s[..i]; }
}
s
}
// This would NOT compile โ dangling reference:
// fn dangling() -> &str {
// let s = String::from("hello");
// &s // ERROR: s dropped here, reference would dangle
// }
fn main() {
let s1 = String::from("hello world");
let result;
{
let s2 = String::from("hi");
result = longest(&s1, &s2);
println!("longest: {}", result);
}
// result can't be used here if it pointed to s2 (which is dropped)
let msg = Important::new("urgent");
println!("important: {}", msg.content());
println!("first word: {}", first_word("hello world"));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_longest() {
assert_eq!(longest("hello", "hi"), "hello");
assert_eq!(longest("a", "bb"), "bb");
}
#[test]
fn test_first_element() {
assert_eq!(first_element(&[1, 2, 3]), Some(&1));
assert_eq!(first_element(&[]), None);
}
#[test]
fn test_important() {
let msg = Important::new("test");
assert_eq!(msg.content(), "test");
}
#[test]
fn test_first_word() {
assert_eq!(first_word("hello world"), "hello");
assert_eq!(first_word("single"), "single");
}
}
(* 105: Lifetime Basics *)
(* OCaml: GC manages lifetimes automatically. No annotations needed. *)
(* Approach 1: Return reference to data โ always safe in OCaml *)
let first_element lst =
match lst with [] -> None | x :: _ -> Some x
let longest s1 s2 =
if String.length s1 >= String.length s2 then s1 else s2
(* Approach 2: Closures capturing references โ GC keeps them alive *)
let make_greeter name =
fun greeting -> Printf.sprintf "%s, %s!" greeting name
(* Approach 3: References to local data โ GC prevents dangling *)
let create_and_use () =
let data = [1; 2; 3] in
let f = fun () -> List.hd data in
f () (* data still alive because GC tracks it *)
(* Tests *)
let () =
assert (first_element [1; 2; 3] = Some 1);
assert (longest "hello" "hi" = "hello");
let greet = make_greeter "World" in
assert (greet "Hello" = "Hello, World!");
assert (create_and_use () = 1);
Printf.printf "โ All tests passed\n"