घोषणात्मक स्मृति प्रबंधन

(एक विशाल भावनात्मक लेख का एक नि: शुल्क अनुवाद जो व्यवहार में व्यापार समस्याओं को हल करने और मैनुअल मेमोरी प्रबंधन से संबंधित बग को हल करने के संदर्भ में सी और रस्ट की संभावनाओं को पाटता है। यह कचरा संग्रह में अनुभव वाले लोगों के लिए भी उपयोगी होना चाहिए - शब्दार्थ के संदर्भ में कई अंतर हैं। इससे कम लग सकता है - लगभग। )


जिस क्षण से मुझे रुस्त में दिलचस्पी हो गई, वह एक अनंत काल की तरह लग रहा था। फिर भी, मुझे स्पष्ट रूप से उधार लेने वाले विश्लेषक (उधारकर्ता चेकर) को जानना याद है , इसके बाद बीसी के रूप में संदर्भित किया जाता है - लगभग प्रति। ), एक सिरदर्द और निराशा से भरा हुआ। बेशक, मैं केवल एक ही पीड़ित नहीं हूं - वॉरहेड्स के साथ संचार के विषय पर इंटरनेट पर बहुत सारे लेख हैं। हालांकि, मैं इस लेख में व्यावहारिक लाभों के दृष्टिकोण से युद्धपोत को खड़ा करना और उजागर करना चाहूंगा, न कि सिर्फ एक सिरदर्द जनरेटर।


समय-समय पर, मेरी राय है कि रस्ट - मैनुअल मेमोरी मैनेजमेंट में ( संभवत: चूंकि यह जीसी के साथ स्वचालित नहीं है, तो और क्या? - लगभग प्रति। ) , लेकिन मैं इस दृष्टिकोण को बिल्कुल भी साझा नहीं करता हूं। रस्ट में उपयोग की जाने वाली विधि, मैं " घोषणात्मक स्मृति प्रबंधन " शब्द को कॉल करता हूं। ऐसा क्यों - अब मैं दिखाऊंगा।


पंजीकरण के लिए नियम


थिअरीज़िंग के बजाय, आइए कुछ उपयोगी लिखें।


ओवरबुक से मिलें - फिक्शन पब्लिशिंग हाउस!


किसी भी प्रकाशक की तरह, ओवरबुक में डिजाइन नियम हैं। अधिक सटीक रूप से, केवल एक नियम है, दरवाजे के रूप में सरल - कोई अल्पविराम नहीं । ओवरबुक का ईमानदारी से मानना ​​है कि अल्पविराम कॉपीराइट आलस्य और परिणाम का एक परिणाम है - "एक घटना के रूप में नष्ट किया जाना चाहिए।" उदाहरण के लिए, वाक्यांश "उसने पढ़ा और हंसा" - अच्छा, उपयुक्त। "वह पढ़ती है, और फिर हँसती है" - सुधार की आवश्यकता है।



यह कहीं भी सरल प्रतीत होता है, हालाँकि, लेखक नियमित रूप से इस नियम का पालन न करने पर ओवरबुक में पकड़ लेते हैं! मानो ऐसा नियम बिलकुल ही नहीं है, अपमानजनक है! हमें हर चीज को दोबारा जांचना होगा। मैन्युअल रूप से। इसके अलावा, यदि संपादन के लिए प्रकाशक द्वारा एक प्रारूप का अनुरोध किया जाता है, तो लेखक एक संस्करण भेज सकता है जिसे एक में तय किया गया है, लेकिन दूसरी जगह भ्रष्ट हो गया है, और इसलिए शुरुआत से ही सब कुछ डबल-चेक किया जाना है। व्यवसाय में काम के समय के लिए इस तरह के लापरवाह रवैये को बर्दाश्त नहीं किया जाता है, और "लेखक के आलस्य" को फँसाने की प्रक्रिया को स्वचालित करने की आवश्यकता उत्पन्न होती है। उदाहरण के लिए, एक कंप्यूटर प्रोग्राम। हाँ, हाँ?


रॉबिन बचाव के लिए पहुंचता है


रॉबिन प्रकाशन घर के कर्मचारियों में से एक है, जिन्होंने कार्यक्रम लिखने में मदद करने के लिए स्वेच्छा से मदद की, क्योंकि वह प्रोग्रामिंग जानता था - यह सौभाग्य है! सच है, रॉबिन सहित विश्वविद्यालय में हर किसी को सी और जावा सिखाया गया था, और जावा वीएम को प्रकाशन गृह से जावा स्थापित करने के लिए अनुचित रूप से मना किया गया था - यह मोड़ है! खैर, इसे C होने दो, C में बहुत कुछ लिखा गया है, भाषा निश्चित और सत्यापित है। सब कुछ वैसा ही चलेगा, जैसा कि 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/hi470129/


All Articles