声明式内存管理

相当大的情感文章相当免费的翻译,实际上在解决业务问题和解决与手动内存管理有关的错误方面,弥合了C和Rust的可能性。这对于有垃圾收集经验的人也应该是有用的-语义方面存在许多差异比看起来少-大约。


从我对Rust感兴趣的那一刻起,它似乎就像是永恒。 不过,我清楚地记得记得了解一下借用分析器( 借用检查器 ,以下简称 BC-大约Per​​。 ),并伴有头痛和绝望。 当然,我并不是唯一一个受苦的人-互联网上有很多关于与弹头通信的文章。 但是,我想从实际利益的角度出发,在本文中突出弹头,而不仅仅是头痛的问题。


有时,我会在Rust中发现以下观点:手动内存管理( 可能不是自动使用GC自动管理,然后还有什么?-大约每秒钟 ),但是,我完全不同意这种观点。 在Rust中使用的方法,我称其为“ 声明性内存管理 ”。 为什么-现在我将展示。


登记规则


不用理论化,我们来写一些有用的东西。


认识超本书 -小说出版社!


像任何出版商一样,Overbook也有设计规则。 更准确地说,只有一个规则,像门一样简单- 没有逗号 。 Overbuk真诚地认为逗号是版权懒惰的结果,并且-引用-“应将其消除为一种现象。” 例如,短语“她读并笑”-好,合适。 “她读了,然后笑了”-需要纠正。



似乎没有地方更简单,但是,作者经常在Overbuk中发现病理性不遵守该规则! 仿佛根本不存在这样的规则,太离谱了! 我们必须仔细检查所有内容。 手动。 此外,如果发行人要求草稿进行编辑,那么作者可以发送已固定在一个版本中但在另一个位置已损坏的版本,因此从一开始就必须仔细检查所有内容。 商业不容忍这种对工作时间的疏忽态度,因此需要自动实现自动捕获“作者的懒惰”的过程。 例如,计算机程序。 是的,是吗?


罗宾急救


罗宾(Robin)是该出版社的员工之一,他自愿帮助编写程序,因为他知道编程-这真是好运! 没错,该大学的每个人,包括罗宾(Robin),都接受过C和Java的培训,并且不合理地禁止Java VM从出版社安装Java-那就该了! 好吧,让它成为C,已经用C编写了很多东西,该语言是确定的并经过验证。 一切都会按原样进行,INFA 100%。


#include <stdio.h>

int main() {
    printf(".");
    return 0;
}

. , Makefile:


.PHONY: all

all:
    gcc -Wall -O0 -g main.c -o main
    ./main

:



:


$ make
gcc -Wall -O0 -g main.c -o main
./main
.

. " !"


// : #include directives

struct Mistake {
    // -  :
    char *message;
};
struct CheckResult {
    //   
    char *path;

    // NULL     
    //     
    struct Mistake *mistake;
};
struct CheckResult check(char *path, char *buf) {
    struct CheckResult result;
    result.path = path;
    result.mistake = NULL;

    // TODO(Robin):  
    // TODO(Robin):  

    return result;
}

// : main()

" " — — " , 256 ".


#define BUF_SIZE 256 * 1024

int main() {
    char buf[BUF_SIZE];

    struct CheckResult result = check("sample.txt", buf);
    if (result.mistake != NULL) {
        printf("  !");
        return 1;
    }

    return 0;
}

, , , :


struct CheckResult check(char *path, char *buf) {
    struct CheckResult result;
    result.path = path;
    result.mistake = NULL;

    FILE *f = fopen(path, "r");
    size_t len = fread(buf, 1, BUF_SIZE - 1, f);
    fclose(f);
    //    -  ,   , .
    buf[len] = '\0';

    // TODO(Robin):  

    return result;
}

, *.txt, . - , , ...


, TODO — ? . :


//   malloc
#include <stdlib.h>

// : structs, etc.

struct CheckResult check(char *path, char *buf) {
    struct CheckResult result;
    result.path = path;
    result.mistake = NULL;

    FILE *f = fopen("sample.txt", "r");
    size_t len = fread(buf, 1, BUF_SIZE - 1, f);
    fclose(f);
    buf[len] = '\0';

    //    C99,      
    //  ,  ,    ,
    //   .
    for (size_t i = 0; i < len; i++) {
        if (buf[i] == ',') {
            struct Mistake *m = malloc(sizeof(Mistake));
            m->message = " !";
            result.mistake = m;
            break;
        }
    }

    return result;
}

"" ( — ..) sample.txt:


',  , !

:


$ make
gcc -Wall -O0 -g main.c -o main
./main
  !

sample2.txt — :


     .

:


$ make
gcc -Wall -O0 -g main.c -o main
./main

! .


...


:


, , . , , .
, , . ?

. ". Mistake , , .". — :


struct Mistake {
    char *message;

    //   
    char *location;
}
// ...
struct CheckResult check(char *path, char *buf) {
    // :  'result' 
    // :  

   for (size_t i = 0; i < len; i++) {
        if (buf[i] == ',') {
            struct Mistake *m = malloc(sizeof(struct Mistake));

            // :  
            m->location = &buf[i];

            m->message = " !";
            result.mistake = m;
            break;
        }
    }

    return result;
}

:


void report(struct CheckResult result) {
    printf("\n");
    printf("~ %s ~\n", result.path);
    if (result.mistake == NULL) {
        printf(" !!\n");
    } else {
        // : "%s"   .
        //       12  ,  ,
        printf(
            ": %s: '%.12s'\n",
            result.mistake->message,
            result.mistake->location
        );
    }
}

int main() {
    char buf[BUF_SIZE];
    {
        struct CheckResult result = check("sample.txt", buf);
        report(result);
    }
    {
        struct CheckResult result = check("sample2.txt", buf);
        report(result);
    }
}

«», — . :


$ make
gcc -Wall -O0 -g main.c -o main
./main

~ sample.txt ~
:  !: ',  '

~ sample2.txt ~
 !!

. , , ?


#define MAX_RESULTS 128

// : ,   

int main() {
    char buf[BUF_SIZE];

    struct CheckResult bad_results[MAX_RESULTS];
    int num_results = 0;

    char *paths[] = { "sample2.txt", "sample.txt", NULL };
    for (int i = 0; paths[i] != NULL; i++) {
        char *path = paths[i];
        struct CheckResult result = check(path, buf);
        bad_results[num_results++] = result;
    }

    for (int i = 0; i < num_results; i++) {
        report(bad_results[i]);
    }
}

. — , . .


$ make
gcc -Wall -O0 -g main.c -o main
./main

~ sample2.txt ~
 !!

~ sample.txt ~
:  !: ',  '


:


, ,
, , , ! - — . ?

"!" — — " !"
. , , :


int main() {
    // :  

    //   "sample2.txt", "sample.txt"
    char *paths[] = { "sample.txt", "sample2.txt", NULL };

    for (int i = 0; paths[i] != NULL; i++) {
        //  
    }

    for (int i = 0; i < num_results; i++) {
        report(results[i]);
    }
}
```bash
$ make
gcc -Wall -O0 -g main.c -o main
./main

~ sample.txt ~
:  !: '   '

~ sample2.txt ~
 !!

- . , , :


— , . ! , - — !


- , , , , , :


— , . . .


. — . — .


" -?" — .


//  ,  memcpy
#include <string.h>

struct CheckResult check(char *path, char *buf) {
    // : ,  

    for (size_t i = 0; i < len; i++) {
        if (buf[i] == ',') {
            struct Mistake *m = malloc(sizeof(struct Mistake));

            //   12   "m->location"
            size_t location_len = len - i;
            if (location_len > 12) {
                location_len = 12;
            }
            m->location = malloc(location_len + 1);
            memcpy(m->location, &buf[i], location_len);
            m->location[location_len] = '\0';

            m->message = " !";
            result.mistake = m;
            break;
        }
    }

    return result;
}

, , , sample3.txt:


   ,    

, !


$ make
gcc -Wall -O0 -g main.c -o main
./main

~ sample.txt ~
:  !: ',  '

~ sample2.txt ~
 !!

~ sample3.txt ~
mistake:  : ',    '


, — :


, . ,
. , , . , , , , ?

" ", . , , malloc(), free() . , ? , 100%, - , - - . , .


int main() {
    char buf[BUF_SIZE];

    struct CheckResult results[MAX_RESULTS];
    int num_results = 0;

    // : ,  

    //   !
    for (int i = 0; i < num_results; i++) {
        free(results[i].mistake);
    }
}

. — buf results . , .


— , ?
— , ?
— , . .
— , , ! . , . .
— . ?


. . , . , . :


— . free(), , .


, , - , , , " ", / , .


int main() {
    //   ...

    //      !
    for (int i = 0; i < num_results; i++) {
        free(results[i].mistake->location);
        free(results[i].mistake);
    }
}

— , . free() , , , , .


- , , . . -, , , , . :


— , free(). .


, , , , , , . . . .


— -, , , , .



. , , , . , . , , .
, .


— , . , .


, . . - . , ?


— , , ?
— . , .


. ( !) . , , . .


. , . , . .


— , — , — ?
— , 130 .


, .


#define MAX_RESULTS 128

int main() {
    char buf[BUF_SIZE];

    struct CheckResult results[MAX_RESULTS];

    // ...
}

, , , . results ( , ). , results results. buf , results , , buf , . . MAX_RESULTS 256. .


— … . . , . , - , , . . .


. 256 , , . . .


" ." — , — " Rust."



(, — ..)


cargo new checker src/main.rs. . :


use std::fs::read_to_string;

fn main() {
    let s = read_to_string("sample.txt");
}

", Ruby!" — , - . . , , Ruby. .


", ?"


fn main() {
    let s = read_to_string("sample.txt");
    s.find(",");
}

, :


error[E0599]: no method named `find` found for type `std::result::Result<std::string::String, std::io::Error>` in the current scope
 --> src\main.rs:5:7
  |
5 |     s.find(",");
  |       ^^^^

. — Result<String, Error> find() , String. .


fn main() -> Result<(), std::io::Error> {
    let s = read_to_string("sample.txt")?;
    s.find(",");

    Ok(())
}

, main() , read_to_string() main(), . , , . , find():


fn main() -> Result<(), std::io::Error> {
    let s = read_to_string("sample.txt")?;

    let result = s.find(",");
    println!("Result: {:?}", result);

    Ok(())
}

$ cargo run --quiet
Result: Some(22)

. - , Option::Some(index), , Option::None. ( , — ..):


fn main() -> Result<(), std::io::Error> {
    let path = "sample.txt";
    let s = read_to_string(path)?;

    println!("~ {} ~", path);
    match s.find(",") {
        Some(index) => {
            println!(":  :  №{}", index);
        },
        None => println!(" !!"),
    }

    Ok(())
}

$ cargo run --quiet
~ sample.txt ~
:  :  №22

, , , . .


fn main() -> Result<(), std::io::Error> {
    let path = "sample.txt";
    let s = read_to_string(path)?;

    println!("~ {} ~", path);
    match s.find(",") {
        Some(index) => {
            // ,      ,
            //    12 ,
            //     
            let slice = &s[index..];
            println!(":  : {:?}", slice);
        }
        None => println!(" !!"),
    }
    Ok(())
}

$ cargo run --quiet
~ sample.txt ~
:  : ',  , !'

. malloc()! memcpy()! ( Go , — ..)! {:?}, , , . , free() . , , — s match. (&s[index..]) , , . , , .



main() , , check():


fn main() -> Result<(), std::io::Error> {
    check("sample.txt")?;

    Ok(())
}

fn check(path: &str) -> Result<(), std::io::Error> {
    let s = read_to_string(path)?;

    println!("~ {} ~", path);
    match s.find(",") {
        Some(index) => {
            let slice = &s[index..];
            println!(":  : {:?}", slice);
        }
        None => println!(" !!"),
    }

    Ok(())
}

, . .
, , . :


fn main() -> Result<(), std::io::Error> {
    let mut s = String::with_capacity(256 * 1024);

    check("sample.txt", &mut s)?;

    Ok(())
}

fn check(path: &str, s: &mut String) -> Result<(), std::io::Error> {
    use std::fs::File;
    use std::io::Read;

    s.clear();
    File::open(path)?.read_to_string(s)?;

    println!("~ {} ~", path);
    match s.find(",") {
        Some(index) => {
            let slice = &s[index..];
            println!(":  : {:?}", slice);
        }
        None => println!(" !!"),
    }

    Ok(())
}

:


  • s 256 , .
  • — , : , .
  • , #include, use , : Read read_to_string check().

, , . check() CheckResult, Mistake. Rust CheckResult , Option<Mistake>. , Mistake . :


struct Mistake {
    location: &str,
}

. .


$ cargo run --quiet
error[E0106]: missing lifetime specifier
  --> src\main.rs:10:15
   |
10 |     location: &str,
   |               ^ expected lifetime parameter

"", , " !", , . , , — (— ..). , :


struct Mistake<'a> {
    location: &'a str,
}

, , , , . , , .
, check() Option<Mistake> () (, , — ..):


fn check(path: &str, s: &mut String) -> Result<Option<Mistake>, std::io::Error> {
    use std::fs::File;
    use std::io::Read;

    s.clear();
    File::open(path)?.read_to_string(s)?;

    println!("~ {} ~", path);

    Ok(match s.find(",") {
        Some(index) => {
            let location = &s[index..];
            Some(Mistake { location })
        }
        None => None,
    })
}

, . — , . match — , Ok(match ...) . , main():


fn main() -> Result<(), std::io::Error> {
    let mut s = String::with_capacity(256 * 1024);

    let result = check("sample.txt", &mut s)?;

    if let Some(mistake) = result {
        println!(":  : {:?}", mistake.location);
    }

    Ok(())
}

" , result ", , " ". - if let, , match, !
!


$ cargo run --quiet
error[E0106]: missing lifetime specifier
  --> src\main.rs:17:55
   |
17 | fn check(path: &str, s: &mut String) -> Result<Option<Mistake>, std::io::Error> {
   |                                                       ^^^^^^^ expected lifetime parameter

, . , :


: , , path s.

? , Mistake location: &'a str — . s — . :


//         :
use std::io::Error as E;

fn check(path: &str, s: &'a mut String) -> Result<Option<Mistake<'a>>, E> {
    // ...
}

, . 'a? . ? , ? , ? , .


error[E0261]: use of undeclared lifetime name `'a`
  --> src\main.rs:19:26
   |
19 | fn check(path: &str, s: &'a mut String) -> Result<Option<Mistake<'a>>, E> {
   |                          ^^ undeclared lifetime

error[E0261]: use of undeclared lifetime name `'a`
  --> src\main.rs:19:66
   |
19 | fn check(path: &str, s: &'a mut String) -> Result<Option<Mistake<'a>>, E> {
   |                                                                  ^^ undeclared lifetime

. ? :


struct Mistake<'a> {
    location: &'a str,
}

, , 'a location: &'a str 'a Mistake<'a>, Java. . Java Rust?


fn check<'a>(path: &str, s: &'a mut String) -> Result<Option<Mistake<'a>>, E> {
    // ...
}

, !


$ cargo run --quiet
~ sample.txt ~
:  : ",  , !"

!


, :


fn main() -> Result<(), std::io::Error> {
    let mut s = String::with_capacity(256 * 1024);

    let path = "sample.txt";
    let result = check(path, &mut s)?;

    println!("~ {} ~", path);
    if let Some(mistake) = result {
        println!(":  : {:?}", mistake.location);
    }

    Ok(())
}

struct Mistake<'a> {
    location: &'a str,
}

use std::io::Error as E;

fn check<'a>(path: &str, s: &'a mut String) -> Result<Option<Mistake<'a>>, E> {
    use std::fs::File;
    use std::io::Read;

    s.clear();
    File::open(path)?.read_to_string(s)?;

    Ok(match s.find(",") {
        Some(index) => {
            let location = &s[index..];
            Some(Mistake { location })
        }
        None => None,
    })
}

'a , , . , . report():


fn main() -> Result<(), std::io::Error> {
    let mut s = String::with_capacity(256 * 1024);

    let path = "sample.txt";
    let result = check(path, &mut s)?;
    report(path, result);

    Ok(())
}

fn report(path: &str, result: Option<Mistake>) {
    println!("~ {} ~", path);
    if let Some(mistake) = result {
        println!(":  : {:?}", mistake.location);
    } else {
        println!(" !!");
    }
}

, Mistake, Display .


struct Mistake<'a> {
    // !
    path: &'static str,
    location: &'a str,
}

, , , 'static. , — . check():


fn check<'a>(path: &str, s: &'a mut String) -> Result<Option<Mistake<'a>>, E> {
    use std::fs::File;
    use std::io::Read;

    s.clear();
    File::open(path)?.read_to_string(s)?;

    Ok(match s.find(",") {
        Some(index) => {
            let location = &s[index..];
            // !
            Some(Mistake { path, location })
        }
        None => None,
    })
}

:


$ cargo run --quiet
error[E0621]: explicit lifetime required in the type of `path`
  --> src\main.rs:37:28
   |
27 | fn check<'a>(path: &str, s: &'a mut String) -> Result<Option<Mistake<'a>>, E> {
   |                    ---- help: add explicit lifetime `'static` to the type of `path`: `&'static str`
...
37 |             Some(Mistake { path, location })
   |                            ^^^^ lifetime `'static` required

// new: &'static
fn check<'a>(path: &'static str, s: &'a mut String) -> Result<Option<Mistake<'a>>, E> {
    // ...
}

Display:


use std::fmt;

impl<'a> fmt::Display for Mistake<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{}:  : {:?}",
            self.path, self.location
        )
    }
}

report():


fn report(result: Option<Mistake>) {
    if let Some(mistake) = result {
        println!("{}", mistake);
    }
}

, :)


$ cargo run --quiet
sample.txt:  : ",  , !"

50 , , . , ?



, . !


fn main() -> Result<(), std::io::Error> {
    let mut s = String::with_capacity(256 * 1024);

    let paths = ["sample.txt", "sample2.txt", "sample3.txt"];
    for path in &paths {
        let result = check(path, &mut s)?;
        report(result);
    }

    Ok(())
}

$ cargo run --quiet
sample.txt:  : ",  , !"
sample3.txt:  :  ",    "

. , , . .


fn main() -> Result<(), std::io::Error> {
    let mut s = String::with_capacity(256 * 1024);

    let paths = ["sample.txt", "sample2.txt", "sample3.txt"];

    //  
    let mut results = vec![];
    for path in &paths {
        let result = check(path, &mut s)?;
        results.push(result);
    }

    //  
    for result in results {
        report(result);
    }

    Ok(())
}

...


$ cargo run --quiet
error[E0499]: cannot borrow `s` as mutable more than once at a time
 --> src\main.rs:9:34
  |
9 |         let result = check(path, &mut s)?;
  |                                  ^^^^^^ mutable borrow starts here in previous iteration of loop

. -? s ??


- . , , s, &s. — , , &mut s. ? , s :


fn check<'a>(path: &'static str, s: &'a mut String) -> Result<Option<Mistake<'a>>, E> {
    //     `s` - 
    s.clear();

    //       `s` -  !
    File::open(path)?.read_to_string(s)?;

    //  .
}

, . , ...


Rust.
.
, !
, Rust !!


.
. , , . memcpy(), -.


Mistake. location , . Mistake , . Rust?


String, , &str . , , . .


struct Mistake<'a> {
    //    ,   
    path: &'static str,

    //      Mistake
    location: String,
}

, , :


error[E0392]: parameter `'a` is never used
  --> src\main.rs:27:16
   |
27 | struct Mistake<'a> {
   |                ^^ unused parameter
   |
   = help: consider removing `'a` or using a marker such as `std::marker::PhantomData`

"-, ", , <'a> . 'a Display check():


struct Mistake {
    //    ,   
    path: &'static str,

    //      Mistake
    location: String,
}
// ...
impl fmt::Display for Mistake {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{}:  : {:?}",
            self.path, self.location
        )
    }
}
// ...
fn check(path: &'static str, s: &mut String) -> Result<Option<Mistake>, E> {
    s.clear();
    File::open(path)?.read_to_string(s)?;

    Ok(match s.find(",") {
        Some(index) => {
            let location = &s[index..];
            Some(Mistake { path, location })
        }
        None => None,
    })
}

, , :


error[E0308]: mismatched types
  --> src\main.rs:43:34
   |
43 |             Some(Mistake { path, location })
   |                                  ^^^^^^^^
   |                                  |
   |                                  expected struct `std::string::String`, found &str
   |                                  help: try using a conversion method: `location: location.to_string()`
   |
   = note: expected type `std::string::String`
              found type `&str`

. location s. , . , . , , "Rust" "", . — ?


" malloc(). , - ! ?" , . :



" . ToString , ToOwned — , . :


fn check(path: &'static str, s: &mut String) -> Result<Option<Mistake>, E> {
    s.clear();
    File::open(path)?.read_to_string(s)?;

    Ok(match s.find(",") {
        Some(index) => {
            let location = s[index..].to_string();
            Some(Mistake { path, location })
        }
        None => None,
    })
}

```bash
$ cargo run --quiet
sample.txt:  : ",  , !"
sample3.txt:  :  ",    "

. , , , , .



Rust- , . , , . , , , , . — !


,
. , , , , , , . , , .

". . ."


. , , , ...


" ..." , . -, - , - , , . , , , , .


". . ."


struct Mistake {
    path: &'static str,
    locations: Vec<String>,
}

, ( , !). , , , , , , , , , -. , , find() .


", find , . - . ?"


'str :


fn check(path: &'static str, s: &mut String) -> Result<Option<Mistake>, E> {
    s.clear();
    File::open(path)?.read_to_string(s)?;

    let locations: Vec<_> = s
        .match_indices(",")
        .map(|(index, slice)| slice.to_string())
        .collect();

    Ok(if locations.is_empty() {
        None
    } else {
        Some(Mistake { path, locations })
    })
}

", if !" , , . , , . map() collect().


Display, :


impl fmt::Display for Mistake {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for location in &self.locations {
            write!(f, "{}:  : {:?}\n", self.path, location)?;
        }
        Ok(())
    }
}

sample.txt :


,  , !
    !
  ́ ́  —
 !

   ,    
  .
    
  .

" , , ", . , .


$ cargo run --quiet
sample4.txt:  : ","
sample4.txt:  : ","
sample4.txt:  : ","

. . . . . " location , ?"


    // check():
    let locations: Vec<_> = s
        .match_indices(",")
        .map(|(index, _)| {
            use std::cmp::{max, min};
            let start = max(0, index - 12);
            let end = min(index + 12, s.len());
            s[start..end].to_string()
        })
        .collect();

.: . , , ASCII, 21 UTF- .

(.: , .)


$ cargo run --quiet
sample4.txt:  : ",  "
sample4.txt:  : " , !"
sample4.txt:  : "   ,    "

" , - ." . " , -, , , . check(), report(). - — , , ."


" , , , , ."



.
. check() , report() — .
. — , .
:


struct Mistake {
    path: &'static str,

    text: String,
    locations: Vec<usize>,
}

fn check(path: &'static str) -> Result<Option<Mistake>, E> {
    let text = std::fs::read_to_string(path)?;

    let locations: Vec<_> = text.match_indices(",").map(|(index, _)| index).collect();

    Ok(if locations.is_empty() {
        None
    } else {
        Some(Mistake { path, text, locations })
    })
}

. , , Mistake, ( ?).
( UTF-8? — ..)


Mistake.


$ cargo run --quiet
warning: field is never used: `text`
  --> src\main.rs:28:5
   |
28 |     text: String,
   |     ^^^^^^^^^^^^
   |
   = note: #[warn(dead_code)] on by default

sample4.txt:  : 1
sample4.txt:  : 19
sample4.txt:  : 83

, - . . :


impl Mistake {
    fn line_bounds(&self, index: usize) -> (usize, usize) {
        let len = self.text.len();

        let before = &self.text[..index];
        let start = before.rfind("\n").map(|x| x + 1).unwrap_or(0);

        let after = &self.text[index + 1..];
        let end = after.find("\n").map(|x| x + index + 1).unwrap_or(len);

        (start, end)
    }
}

" rfind(). , , , unwrap_or()."


, :


impl fmt::Display for Mistake {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for &location in &self.locations {
            let (start, end) = self.line_bounds(location);
            let line = &self.text[start..end];
            write!(f, "{}:  :\n", self.path)?;
            write!(f, "\n")?;
            write!(f, "      | {}", line)?;
            write!(f, "\n\n")?;
        }
        Ok(())
    }
}

$ cargo run --quiet
sample4.txt:  :

      | ,  , !

sample4.txt:  :

      | ,  , !

sample4.txt:  :

      |    ,    

. !


, - :


impl fmt::Display for Mistake {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for &location in &self.locations {
            let (start, end) = self.line_bounds(location);
            let line = &self.text[start..end];

            let line_number = self.text[..start].matches("\n").count() + 1;

            write!(f, "{}:  :\n", self.path)?;
            write!(f, "\n")?;
            write!(f, "{:>8} | {}", line_number, line)?;
            write!(f, "\n\n")?;
        }
        Ok(())
    }
}

$ cargo run --quiet
sample4.txt:  :

      1 | ,  , !

sample4.txt:  :

      1 | ,  , !

sample4.txt:  :

      6 |    ,    

( , Rust ?)


— , . , Rust, ^. — Display, , , , , . 11 ( 8 + |, 3 ):
( technic93, utf-8, , , , . , — ..)


impl fmt::Display for Mistake {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for &location in &self.locations {
            let (start, end) = self.line_bounds(location);
            let line = &self.text[start..end];

            let line_number = self.text[..start].matches("\n").count() + 1;
            let comma_index = self.text[start..location].chars().count();

            write!(f, "{}:  :\n\n", self.path)?;

            //    
            write!(f, "{:>8} | {}\n", line_number, line)?;

            //  
            write!(f, "{}^\n\n", " ".repeat(11 + comma_index))?;
        }
        Ok(())
    }
}

$ cargo run --quiet
sample4.txt:  :

      1 | ,  , !
           ^

sample4.txt:  :

      1 | ,  , !
                             ^

sample4.txt:  :

      6 |    ,    
                        ^

.


. 85 .


fn main() -> Result<(), std::io::Error> {
    let paths = ["sample4.txt"];

    //  
    let mut results = vec![];
    for path in &paths {
        let result = check(path)?;
        results.push(result);
    }

    //   
    for result in results {
        report(result);
    }

    Ok(())
}

fn report(result: Option<Mistake>) {
    if let Some(mistake) = result {
        println!("{}", mistake);
    }
}

struct Mistake {
    path: &'static str,

    text: String,
    locations: Vec<usize>,
}

use std::io::Error as E;

fn check(path: &'static str) -> Result<Option<Mistake>, E> {
    let text = std::fs::read_to_string(path)?;

    let locations: Vec<_> = text.match_indices(",").map(|(index, _)| index).collect();

    Ok(if locations.is_empty() {
        None
    } else {
        Some(Mistake {
            path,
            text,
            locations,
        })
    })
}

use std::fmt;

impl Mistake {
    fn line_bounds(&self, index: usize) -> (usize, usize) {
        let len = self.text.len();

        let before = &self.text[..index];
        let start = before.rfind("\n").map(|x| x + 1).unwrap_or(0);

        let after = &self.text[index + 1..];
        let end = after.find("\n").map(|x| x + index + 1).unwrap_or(len);

        (start, end)
    }
}

impl fmt::Display for Mistake {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for &location in &self.locations {
            let (start, end) = self.line_bounds(location);
            let line = &self.text[start..end];

            let line_number = self.text[..start].matches("\n").count() + 1;
            let comma_index = self.text[start..location].chars().count();

            write!(f, "{}:  :\n\n", self.path)?;

            //    
            write!(f, "{:>8} | {}\n", line_number, line)?;

            //  
            write!(f, "{}^\n\n", " ".repeat(11 + comma_index))?;
        }
        Ok(())
    }
}


, .


. , Rust. " Rust ", , , . , , , , .


( , FFI.)

Source: https://habr.com/ru/post/zh-CN470129/


All Articles