this post was submitted on 20 Mar 2024
17 points (100.0% liked)

Learning Rust and Lemmy

393 readers
1 users here now

Welcome

A collaborative space for people to work together on learning Rust, learning about the Lemmy code base, discussing whatever confusions or difficulties we're having in these endeavours, and solving problems, including, hopefully, some contributions back to the Lemmy code base.

Rules TL;DR: Be nice, constructive, and focus on learning and working together on understanding Rust and Lemmy.


Running Projects


Policies and Purposes

  1. This is a place to learn and work together.
  2. Questions and curiosity is welcome and encouraged.
  3. This isn't a technical support community. Those with technical knowledge and experienced aren't obliged to help, though such is very welcome. This is closer to a library of study groups than stackoverflow. Though, forming a repository of useful information would be a good side effect.
  4. This isn't an issue tracker for Lemmy (or Rust) or a place for suggestions. Instead, it's where the nature of an issue, what possible solutions might exist and how they could be or were implemented can be discussed, or, where the means by which a particular suggestion could be implemented is discussed.

See also:

Rules

  1. Lemmy.ml rule 2 applies strongly: "Be respectful, even when disagreeing. Everyone should feel welcome" (see Dessalines's post). This is a constructive space.
  2. Don't demean, intimidate or do anything that isn't constructive and encouraging to anyone trying to learn or understand. People should feel free to ask questions, be curious, and fill their gaps knowledge and understanding.
  3. Posts and comments should be (more or less) within scope (on which see Policies and Purposes above).
  4. See the Lemmy Code of Conduct
  5. Where applicable, rules should be interpreted in light of the Policies and Purposes.

Relevant links and Related Communities


Thumbnail and banner generated by ChatGPT.

founded 1 year ago
MODERATORS
 

Intro

Not long ago I posed a challenge for those of us learning rust: https://lemmy.ml/post/12478167.

Basically write an equivalent of git diff --no-index A B ... a file differ.

While it's never too late to attempt it, I figured it'd be a good time to check in to see what anyone thought of it, in part because some people may have forgotten about it and would still like to have a shot, and also because I had a shot and am happy with what I wrote.

Check In

I'll post where I got up to below (probably as a comment), but before that, does anyone have anything to share on where they got up to ... any general thoughts on the challenge and the general idea of these?

My experience

My personal experience was that I'd not kept up with my rust "studies" for a bit and used this as a good "warm up" or "restart" exercise and it worked really well. Obviously learning through doing is a good idea, and the Rust Book is a bit too light, IMO, on good exercises or similar activities. But I found this challenge just difficult enough to make me feel more comfortable with the language.

Future Challenges

Any ideas for future challenges??

My quick thoughts

  • A simple web app like a todo app using axtix_web and diesel and some templating crate.
  • Extend my diffing program to output JSON/HTML and then to diff by characters in a string
  • A markdown parser??
you are viewing a single comment's thread
view the rest of the comments
[–] [email protected] 1 points 1 year ago

... continued

fn main() {
    // > Getting args
    let args: Vec<String> = env::args().collect();

    if args[1..].len() != 2 {
        panic!(
            "Must provide two paths. Instead provided {:}",
            args[1..].len()
        );
    }

    println!("Args:");
    for a in args[1..].iter() {
        println!("{}", a);
    }

    // > Reading files and splitting into lines

    let a = read_to_string(&args[1]).expect("Failed to read file");
    let b = read_to_string(&args[2]).expect("Failed to read file");
    let a_lines: Vec<&str> = a.split("\n").collect();
    let b_lines: Vec<&str> = b.split("\n").collect();

    // > Initialising globals

    let file_lengths = FileLens::new(&a_lines, &b_lines);

    let cursor = Cursors { a: 0, b: 0 };
    let mut chunks: Vec<Chunk> = vec![];

    // mut necessary as overwriting in each loop
    let mut state: State = State::NewChunk {cursor};

    // > Loop
    loop {
        state = match state {
            State::NewChunk { cursor } => State::NewLine {
                chunk_data: ChunkData{
                    lines: Vec::new(), cursor: cursor.clone(), start_cursor: cursor}
            },
            State::NewLine { chunk_data } => {
                let line_read = read_line(&chunk_data.cursor, &file_lengths, &a_lines, &b_lines);

                match chunk_data.lines.as_slice() {
                    [] => {
                        match line_read.kind {
                            CompType::Match => State::ContinuingChunk {
                                chunk_data, line_read },
                            CompType::Change => State::ContinuingChunk {
                                chunk_data, line_read },
                            CompType::FileEnd(file_spec) => State::FileEnd {
                                chunk_data, line_read: file_spec},
                        }
                    },
                    [.., lc] => {
                        match lc.kind {
                            CompType::Match => {
                                match line_read.kind {
                                    CompType::Match => State::ContinuingChunk {
                                        chunk_data, line_read },
                                    CompType::Change => State::EndingChunk {
                                        chunk_data, line_read },
                                    CompType::FileEnd(file_spec) => State::FileEnd {
                                        chunk_data, line_read: file_spec},
                                }
                            }
                            CompType::Change => {
                                match line_read.kind {
                                    CompType::Match => State::EndingChunk {
                                        chunk_data, line_read },
                                    CompType::Change => State::ContinuingChangeChunk {
                                        chunk_data, line_read },
                                    CompType::FileEnd(_) => State::FileEndChange {
                                        chunk_data},
                                }
                            }
                            CompType::FileEnd(_) => panic!(
                            // error! should not have come here from FileEnd
                                "Failed to process file end correctly (failed at lines a:{},b:{})",
                                line_read.cursor.a, line_read.cursor.b),
                        }
                    }
                }
            },
            State::ContinuingChunk { mut chunk_data, line_read } => {
                chunk_data.lines.push(line_read);
                let new_cursor = chunk_data.cursor.increment_cursor(None);
                chunk_data.set_cursor(new_cursor);
                State::NewLine{chunk_data}
            },
            State::ContinuingChangeChunk { chunk_data, line_read } => {
                let first_lc = chunk_data.lines.first().unwrap();
                if a_lines[first_lc.cursor.a] == b_lines[line_read.cursor.b] {
                    State::EndingChangedChunk{
                        chunk_data, chunk_type: ChunkType::Addition
                    }
                }
                else if a_lines[line_read.cursor.a] == b_lines[first_lc.cursor.b] {
                    State::EndingChangedChunk{
                        chunk_data, chunk_type: ChunkType::Deletion
                    }
                }
                else {
                    State::ContinuingChunk { chunk_data, line_read }
                }
            },
            State::EndingChunk { chunk_data, line_read } => {
                let chunk_type = match line_read.kind {
                    CompType::Match => ChunkType::Modification,
                    CompType::Change => ChunkType::Match,
                    CompType::FileEnd(_) => panic!(
                            // error! should not have come here from FileEnd
                            "Failed to process file end correctly (failed at lines a:{},b:{})",
                            line_read.cursor.a, line_read.cursor.b
                        )
                };
                let new_a_lines: Vec<String> = chunk_data.lines.iter()
                                                .map(|lc| String::from(a_lines[lc.cursor.a]))
                                                .collect();
                let new_b_lines: Vec<String> = chunk_data.lines.iter()
                                                .map(|lc| String::from(b_lines[lc.cursor.b]))
                                                .collect();
                chunks.push(
                    Chunk{
                        chunk_type,
                        a_lines: new_a_lines,
                        b_lines: new_b_lines,
                        cursor: chunk_data.start_cursor,
                    }
                );

                // continue from last read line, but with a new chunk
                // ... repetitive yes, but cleaner code I think
                State::NewChunk { cursor: line_read.cursor }
            },
            State::EndingChangedChunk { chunk_data, chunk_type } => {

                let new_a_lines: Vec<String> = chunk_data.lines.iter()
                                                .map(|lc| String::from(a_lines[lc.cursor.a]))
                                                .collect();
                let new_b_lines: Vec<String> = chunk_data.lines.iter()
                                                .map(|lc| String::from(b_lines[lc.cursor.b]))
                                                .collect();
                chunks.push(
                    Chunk{
                        chunk_type,
                        a_lines: new_a_lines,
                        b_lines: new_b_lines,
                        cursor: chunk_data.start_cursor,
                    }
                );
                let new_cursor = chunk_data.cursor.increment_cursor(Some(chunks.last().unwrap()));

                State::NewChunk { cursor: new_cursor }
            },
            State::FileEnd { chunk_data, line_read} => {
                match line_read {
                    FileSpec::A => {
                        chunks.push(
                            Chunk{
                                chunk_type: ChunkType::Addition,
                                a_lines: vec![],
                                b_lines: b_lines[chunk_data.cursor.b..].iter()
                                            .map(|s| String::from(*s)).collect(),
                                cursor: chunk_data.cursor,
                            }
                        );
                        State::End
                    },
                    FileSpec::B => {
                        chunks.push(
                            Chunk{
                                chunk_type: ChunkType::Deletion,
                                a_lines: a_lines[chunk_data.cursor.a..].iter()
                                            .map(|s| String::from(*s)).collect(),
                                b_lines: vec![],
                                cursor: chunk_data.cursor,
                            }
                        );
                        State::End
                    },
                    FileSpec::AB => State::End,
                }
            },
            State::FileEndChange { chunk_data } => {
                let a_cursor = chunk_data.start_cursor.a.clone();
                let b_cursor = chunk_data.start_cursor.b.clone();
                chunks.push(
                    Chunk{
                        chunk_type: ChunkType::Deletion,
                        a_lines: a_lines[a_cursor..].iter()
                                    .map(|s| String::from(*s)).collect(),
                        b_lines: vec![],
                        cursor: chunk_data.start_cursor,
                    }
                );
                chunks.push(
                    Chunk{
                        chunk_type: ChunkType::Addition,
                        a_lines: vec![],
                        b_lines: b_lines[b_cursor..].iter()
                                    .map(|s| String::from(*s)).collect(),
                        cursor: chunk_data.cursor,
                    }
                );
                State::End
            },
            State::End => break,
        };

    }

    // > Wrap up

    println!("Done!");

    for (i,c) in chunks.iter().enumerate() {
        println!("\n--- Chunk: {} ---", i);
        println!("Type: {:?}", c.chunk_type);
        println!("A: {:?}", c.a_lines);
        println!("B: {:?}", c.b_lines);
    }
}