( ترجمة مجانية لمقال عاطفي كبير يعمل في الواقع على سد إمكانيات C و Rust فيما يتعلق بحل مشاكل الأعمال وحل الأخطاء المتعلقة بالإدارة اليدوية للذاكرة. ينبغي أن يكون مفيدًا أيضًا للأشخاص ذوي الخبرة في جمع القمامة - هناك اختلافات كثيرة من حيث الدلالات أقل مما قد يبدو - تقريبا لكل. )
منذ اللحظة التي أصبحت فيها مهتمة بالصدأ ، بدا الأمر وكأنه إلى الأبد. ومع ذلك ، أتذكر بوضوح التعرف على محلل الاقتراض ( مدقق الاقتراض ، المشار إليه فيما بعد بـ BC - تقريبًا لكل. ) ، مصحوبًا بصداع ويأس. بالطبع ، لست المعاناة الوحيدة - فهناك الكثير من المقالات على الإنترنت حول موضوع التواصل مع الرؤوس الحربية. ومع ذلك ، أود أن أبرز وأبرز في هذه المقالة الرؤوس الحربية من وجهة نظر الفوائد العملية ، وليس مجرد مولد الصداع.
من وقت لآخر ، أجد آراءًا في Rust - الإدارة اليدوية للذاكرة ( ربما لأنها ليست تلقائية مع GC ، ثم ماذا؟ - تقريبًا لكل منهما ) ، لكنني لا أشارك وجهة النظر هذه على الإطلاق. الطريقة المستخدمة في الصدأ ، وأنا أسمي مصطلح " إدارة الذاكرة التعريفي ". لماذا هكذا - الآن سوف تظهر.
بدلاً من التنظير ، لنكتب شيئًا مفيدًا.
تلبية Overbook - دار نشر الخيال!
مثل أي ناشر ، يحتوي Overbook على قواعد تصميم. بتعبير أدق ، هناك قاعدة واحدة فقط ، بسيطة مثل الأبواب - لا توجد فواصل . يعتقد Overbuk بإخلاص أن الفواصل هي نتيجة لكسل حقوق التأليف والنشر و- الاقتباس - "يجب القضاء عليها كظاهرة". على سبيل المثال ، عبارة "قرأت وضحكت" - جيدة ومناسبة. "لقد قرأت ، ثم ضحكت" - يتطلب تصحيحًا.

قد يبدو الأمر أكثر بساطة في أي مكان ، ومع ذلك ، يصطاد المؤلفون بانتظام في Overbuk حول عدم الامتثال المرضي لهذه القاعدة! وكأن هذه القاعدة غير موجودة على الإطلاق ، فاحشة! علينا أن نتحقق من كل شيء. يدويا. علاوة على ذلك ، إذا طلب الناشر مسودة للتحرير ، يمكن للمؤلف إرسال نسخة تم إصلاحها في واحدة ، ولكن تالفة في مكان آخر ، وبالتالي يجب فحص كل شيء من البداية. لا تتسامح الأعمال مع هذا الموقف المهمل في وقت العمل ، وقد نشأت الحاجة إلى أتمتة عملية التقاط "كسل المؤلف" بمفرده. على سبيل المثال ، برنامج كمبيوتر. نعم نعم
روبن يسارع إلى الإنقاذ
روبن هو أحد موظفي دار النشر الذين تطوعوا للمساعدة في كتابة البرنامج ، لأنه كان يعرف البرمجة - هذا حظ سعيد! صحيح أن الجميع في الجامعة ، بما في ذلك روبن ، تم تدريسهم 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
.
. " !"
struct Mistake {
char *message;
};
struct CheckResult {
char *path;
struct Mistake *mistake;
};
struct CheckResult check(char *path, char *buf) {
struct CheckResult result;
result.path = path;
result.mistake = NULL;
return result;
}
" " — — " , 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';
return result;
}
, *.txt, . - , , ...
, TODO — ? . :
#include <stdlib.h>
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';
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) {
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 {
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() {
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 ~
!!
- . , , :
— , . ! , - — !
- , , , , , :
— , . . .
. — . — .
" -?" — .
#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));
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) => {
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
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.clear();
File::open(path)?.read_to_string(s)?;
}
, . , ...
Rust.
.
, !
, Rust !!
.
. , , . memcpy()
, -.
Mistake
. location
, . Mistake
, . Rust?
— String
, , &str
. , , . .
struct Mistake<'a> {
path: &'static str,
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,
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
, ?"
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:
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.)