(Eine ziemlich kostenlose Ăbersetzung eines riesigen emotionalen Artikels, der in der Praxis die Möglichkeiten von C und Rust in Bezug auf die Lösung von GeschĂ€ftsproblemen und die Behebung von Fehlern im Zusammenhang mit der manuellen Speicherverwaltung ĂŒberbrĂŒckt. Sie sollte auch fĂŒr Personen mit Erfahrung in der Speicherbereinigung nĂŒtzlich sein - es gibt viele Unterschiede in Bezug auf die Semantik weniger als es scheint - ca. per. )
Von dem Moment an, als ich mich fĂŒr Rust interessierte, schien es eine Ewigkeit zu sein. Trotzdem erinnere ich mich noch genau daran, wie ich den Kreditanalysator ( KreditprĂŒfer , im Folgenden als BC bezeichnet - ca. Per. ) Kennen gelernt habe, begleitet von Kopfschmerzen und Verzweiflung. NatĂŒrlich bin ich nicht der einzige, der leidet - es gibt viele Artikel im Internet zum Thema Kommunikation mit Sprengköpfen. Ich möchte jedoch in diesem Artikel den Sprengkopf unter dem Gesichtspunkt der praktischen Vorteile hervorheben und hervorheben und nicht nur einen Kopfschmerzgenerator.
Von Zeit zu Zeit stoĂe ich auf Meinungen, die in Rust - manuelle Speicherverwaltung ( wahrscheinlich, da sie mit GC nicht automatisch erfolgt, was dann noch? - ca. Per. ), Aber ich teile diesen Standpunkt ĂŒberhaupt nicht. Die in Rust verwendete Methode nenne ich den Begriff " deklarative Speicherverwaltung ". Warum so - jetzt werde ich zeigen.
Anstatt zu theoretisieren, schreiben wir etwas NĂŒtzliches.
Treffen Sie Overbook - Fiction Publishing House!
Wie jeder Verlag hat Overbook Designregeln. Genauer gesagt gibt es nur eine Regel, einfach wie TĂŒren - keine Kommas . Overbuk glaubt aufrichtig, dass Kommas eine Folge der Faulheit des Urheberrechts sind und - Zitat - "als PhĂ€nomen ausgerottet werden sollten". Zum Beispiel der Satz "Sie las und lachte" - gut, passend. "Sie las und lachte dann" - muss korrigiert werden.

Nirgendwo scheint es einfacher zu sein, aber die Autoren melden sich regelmĂ€Ăig in Overbuk ĂŒber die pathologische Nichteinhaltung dieser Regel! Als ob eine solche Regel ĂŒberhaupt nicht existiert, empörend! Wir mĂŒssen alles noch einmal ĂŒberprĂŒfen. Manuell. Wenn der Herausgeber einen Entwurf zur Bearbeitung anfordert, kann der Autor auĂerdem eine Version senden, die an einer Stelle repariert, an einer anderen Stelle jedoch beschĂ€digt wurde. Daher muss von Anfang an alles ĂŒberprĂŒft werden. Unternehmen tolerieren eine derart fahrlĂ€ssige Einstellung zur Arbeitszeit nicht, und es bestand die Notwendigkeit, den Prozess des Auffangens der "Faulheit des Autors" von selbst zu automatisieren. Zum Beispiel ein Computerprogramm. Ja ja
Robin eilt zur Rettung
Robin ist einer der Mitarbeiter des Verlags, der sich freiwillig fĂŒr das Schreiben des Programms gemeldet hat, weil er sich mit Programmierung auskannte - das ist viel GlĂŒck! Zwar wurde allen an der UniversitĂ€t, einschlieĂlich Robin, C und Java beigebracht, und Java VM war es unangemessen verboten, Java vom Verlag zu installieren - das ist an der Reihe! Nun, sei es C, es wurde viel in C geschrieben, die Sprache ist sicher und verifiziert. Alles wird so laufen, wie es sollte, 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.)