惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
Apple Machine Learning Research
Apple Machine Learning Research
博客园_首页
爱范儿
爱范儿
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
罗磊的独立博客
M
MIT News - Artificial intelligence
D
Docker
量子位
T
Tailwind CSS Blog
人人都是产品经理
人人都是产品经理
月光博客
月光博客
有赞技术团队
有赞技术团队
J
Java Code Geeks
A
About on SuperTechFans
P
Proofpoint News Feed
Jina AI
Jina AI
Y
Y Combinator Blog
T
The Blog of Author Tim Ferriss
The GitHub Blog
The GitHub Blog
Microsoft Security Blog
Microsoft Security Blog
V
V2EX
GbyAI
GbyAI
F
Fortinet All Blogs

The Rust Programming Language Forum - Latest topics

Beginner building a Rust backend framework (AI-assisted) — feedback appreciated C++ to Rust -- Exceptions Re-exporting a trait with a custom derive_macro Gold linker is deprecated Why is using PhantomData valid in this case? Code review for Static Pool Allocator Looking for a pool based allocator Why is this legal? What does it mean for Rustc to run "out of TLS keys"? Using async/await internally with no internal runtime, but exposing a nonblocking poll API — is this a reasonable design? Testing functions that use randomness `cargo-path`: improve coding agents&#39; ability to find Rust documentation Rust task runners Lifetime weird case Windows - USB device not detected A random rustc-ice-[...].txt file appeared Develop rust where the environment is setup in a docker Undefined Behavior: in-bounds pointer arithmetic failed: attempting to offset pointer by 20 bytes, but got alloc238 which is only 1 byte from the end of the allocation Unbug 0.5 - Runtime debug assertions Is there a tiny error in section 6.2 Reference types? Lifetime woes implementing ratatui::Widget for a reference Rusqlite + Chrono: How do I simplify code to obtain chrono datetime value From OOP to Rust – struggling with code organization and data structure design Way to avoid a self-referential struct Rust RF and audio resources/communities Whyhttp - HTTP mocks that fail where the bug actually is Using tokio channel permits in a tower service Arc::increment_strong_count design question (cross-post) `&T`, `&mut T`, `Pin<&mut T>` and `&Cell<T>`: Ways of Borrowing a `T` Ratatui detect arrow key press and release
Loops with accumulated state confuse me
RobertN · 2026-04-16 · via The Rust Programming Language Forum - Latest topics

While @jdahlstrom’s solution is a really elegant one, I would like to point out a more general perspective.

This is the first half of the problem, and the simpler one.

Basically, this is a nested sequence: a sequence of sequences, an array of arrays, two nested loops, or a generator function returning generators. There are many manifestations of this concept.

They all share the idea of flattening: somewhere in the program, you only see the inner item. In your example, that is the body of the nested for loop. That is one of many possible solutions, and it is perfectly fine.

This is the second half of the problem.

“When it changes” means that you are effectively processing the sequence in pairs. There is some function or logic that takes two elements from the (abstract) sequence as parameters.

And at the end, your implicit requirement is that this function or logic should still be called with the last element of the sequence as the first parameter, even though there is obviously no element available for the second parameter. That missing thing is the root cause of the awkward repeated if statement in your example.

A very old technique, with its origins, I think, in list processing, is the introduction of sentinel values at the end (and sometimes the beginning) of a sequence. These values can be concrete instances of the original element type, or special values.

An extremely widely adopted sentinel value, for example, is the terminating NULL byte in strings in many programming languages. It is so common that virtually nobody realizes that the NULL is a sentinel. :wink:

In a language like Rust, you can define better sentinel values by using enums, or simply use a pre-made enum like Option. So, given a (flattened) sequence, you add sentinels and then process it.

A more general example:

fn main() {
    let sequence = [1, 2, 3];
    let iter = sequence.iter().map(Some).chain([None]);
    let mut last = None;
    for item in iter {
        match (last, item) {
            (None, Some(x)) => println!("first: {x}"),
            (Some(a), Some(b)) => println!("pair: ({a}, {b})"),
            (Some(x), None) => println!("last: {x}"),
            _ => unreachable!(),
        }
        last = item;
    }
}

And more concretely for your example (note: your original code processed an empty buffer at the start; this one does not):

fn process_buffer(buf: &mut Vec<char>) {
    println!("process_buffer: {buf:?}");
    buf.clear();
}

fn char_language(c: char) -> u32 {
    c as u32
}

fn main() {
    let strs = ["aab", "bbc", "dda"];
    let chars = strs.iter().flat_map(|s| s.chars());
    let mut buf = Vec::new();
    let mut prev_lang = None;
    for c in chars.map(Some).chain([None]) {
        let lang = c.map(char_language);
        if lang != prev_lang && prev_lang.is_some() {
            process_buffer(&mut buf);
        }
        if let Some(c) = c {
            buf.push(c);
        }
        prev_lang = lang;
    }
    assert!(buf.is_empty());
}

The concept of sentinels can be generalized even further. Different sentinel-like values can be inserted into a sequence at different points to carry information forward to subsequent operations on that sequence.