aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFederico Igne <git@federicoigne.com>2022-04-27 21:56:42 +0100
committerFederico Igne <git@federicoigne.com>2022-05-27 00:46:07 +0100
commit394621b3a2290e363a60b372e07ad19c47105ff1 (patch)
treec9171402f3cf9cf521b10071f821916c6630c541
parentcd1dbb23feb4115267c5bc80fb3aa206daf866ea (diff)
downloadpangler-394621b3a2290e363a60b372e07ad19c47105ff1.tar.gz
pangler-394621b3a2290e363a60b372e07ad19c47105ff1.zip
docs: add some notes on recent lifetime learnings
-rw-r--r--src/main.rs81
1 files changed, 58 insertions, 23 deletions
diff --git a/src/main.rs b/src/main.rs
index d03d5f8..a8b8198 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -9,34 +9,66 @@ use pandoc_ast::Block;
9 9
10type Blocks<'a> = HashMap<String,Cow<'a,str>>; 10type Blocks<'a> = HashMap<String,Cow<'a,str>>;
11 11
12fn build_block(blocks: &Blocks, code: &Cow<str>) -> String { 12/*
13 * Here are some notes on the following function
14 *
15 * lazy_static! {
16 * static ref MACRO: Regex = Regex::new(r"regex").unwrap();
17 * }
18 *
19 * let mut text = Cow::from("This is some text...");
20 *
21 * The problem with this version is that due to how `Cow` works, the value returned by
22 * `replace_all` cannot live more than the borrowed `text` passed as a parameter. This is
23 * because the function returns a reference to `text` (Cow::Borrowed) if no replacement takes
24 * place, so for the returned value to be valid, `text` still needs to be available.
25 * But text gets overridden right away, so, in principle, if no replacement takes place `text`
26 * gets overridden by a reference to it (losing data).
27 *
28 * Note that this doesn't happen in practice (but the compiler doesn't know about this) because
29 * the `replace_all` function is applied as long as some replacement is possible (`while`
30 * condition). In other words, all calls to `replace_all` always return an `Cow::Owned` value.
31 *
32 * while MACRO.is_match(&text) {
33 * text = MACRO.replace_all(&text, _closure);
34 * }
35 *
36 * This is how you would solve the problem instead:
37 *
38 * while let Cow::Owned(new_text) = MACRO.replace_all(&text, _closure) {
39 * text = Cow::from(new_text);
40 * }
41 *
42 * In this case, the matched `Cow::Owned` is not concerned by any lifetime (type is `Cow<'_,str>`)
43 * of the borrowed value `text`. Moreover `text` takes ownership of `new_text: String` using
44 * the `Cow::from()` function. No heap allocation is performed, and the string is not copied.
45 *
46 * println!("{}", text)
47 *
48 */
49fn build(blocks: &Blocks) {
13 lazy_static! { 50 lazy_static! {
51 static ref PATH: Regex = Regex::new(r"^(?:[[:word:]\.-]+/)*[[:word:]\.-]+\.[[:alpha:]]+$").unwrap();
14 static ref MACRO: Regex = Regex::new(r"(?m)^([[:blank:]]*)<<([^>\s]+)>>").unwrap(); 52 static ref MACRO: Regex = Regex::new(r"(?m)^([[:blank:]]*)<<([^>\s]+)>>").unwrap();
15 } 53 }
16 if MACRO.is_match(&code) { 54 blocks.iter().for_each(|(k,v)| if PATH.is_match(k) {
17 let code = MACRO.replace_all(&code, |caps: &Captures| { 55 let mut code = v.clone(); // No clone is happening because the value is a `Borrowed`
56 // Here `replace_all` returns a `Owned` value only when a replacement takes place.
57 // We can use it to recursively build blocks of code until no more substitutions are
58 // necessary (i.e., `replace_all` returns a `Borrowed`).
59 while let Cow::Owned(step) = MACRO.replace_all(&code, |caps: &Captures| {
18 let indent = caps[1].len(); 60 let indent = caps[1].len();
19 blocks.get(&caps[2]) 61 blocks.get(&caps[2])
20 .expect("Block not present") 62 .expect("Block not present")
21 .lines() 63 .lines()
22 .map(|l| format!("{:indent$}{}", "", l) ) 64 .map(|l| format!("{:indent$}{}", "", l) )
23 .collect::<Vec<_>>() 65 .collect::<Vec<_>>()
24 .join("\n") 66 .join("\n")
25 } 67 }
26 ); 68 ) {
27 build_block(blocks, &code) 69 code = Cow::from(step);
28 } else { 70 }
29 code.to_string() 71 println!("[[{}]]\n{}", k, code);
30 }
31}
32
33fn build(blocks: &Blocks) {
34 lazy_static! {
35 static ref PATH: Regex = Regex::new(r"^(?:[[:word:]\.-]+/)+[[:word:]\.-]+\.[[:alpha:]]+$").unwrap();
36 }
37 blocks.iter().for_each(|(k,v)| if PATH.is_match(k) {
38 let string = build_block(blocks, v);
39 println!("[[{}]]\n{}", k, string);
40 }) 72 })
41} 73}
42 74
@@ -60,7 +92,10 @@ fn main() -> Result<()> {
60 .find_map(|(k,v)| if k == "path" { Some(v.clone()) } else { None }) 92 .find_map(|(k,v)| if k == "path" { Some(v.clone()) } else { None })
61 .unwrap_or(String::from("")); 93 .unwrap_or(String::from(""));
62 key.push_str(&attr.0); 94 key.push_str(&attr.0);
63 /* Insert (or replace) block of code */ 95 /* Insert (or replace) block of code.
96 * NOTE: we are always "borrowing" the snippets of code with `Cow`, i.e., it
97 * will always be a `Cow::Borrowed` value, until we process the string
98 * substituting macros. */
64 blocks.insert(key, Cow::from(code)); 99 blocks.insert(key, Cow::from(code));
65 } else { 100 } else {
66 // println!("The following code has no ID:"); 101 // println!("The following code has no ID:");