Sử dụng ngôn ngữ chặt chẽ hơn không chỉ chuyển mục tiêu bài viết xung quanh từ việc triển khai chính xác sang làm đúng thông số kỹ thuật. Thật khó để làm một cái gì đó rất sai nhưng vẫn nhất quán về mặt logic; đó là lý do tại sao trình biên dịch bắt được rất nhiều lỗi.
Số học con trỏ như thường được xây dựng là không có cơ sở vì hệ thống loại không thực sự có nghĩa là gì. Bạn có thể tránh vấn đề này hoàn toàn bằng cách làm việc trong một ngôn ngữ được thu gom rác (cách tiếp cận thông thường khiến bạn cũng phải trả tiền cho sự trừu tượng). Hoặc bạn có thể cụ thể hơn nhiều về loại con trỏ bạn đang sử dụng, để trình biên dịch có thể từ chối bất cứ thứ gì không nhất quán hoặc không thể được chứng minh là chính xác như được viết. Đây là cách tiếp cận của một số ngôn ngữ như Rust.
Các kiểu xây dựng tương đương với bằng chứng, vì vậy nếu bạn viết một hệ thống loại mà quên điều này, thì tất cả các loại đều sai. Giả sử trong một thời gian rằng khi chúng ta khai báo một loại, chúng ta thực sự có nghĩa là chúng ta đang khẳng định sự thật về những gì trong biến.
- int * x; // Khẳng định sai. x tồn tại và không trỏ đến một int
- int * y = z; // Chỉ đúng nếu z được chứng minh là trỏ đến một int
- * (x + 3) = 5; // Chỉ đúng nếu (x + 3) trỏ đến một int trong cùng một mảng với x
- int c = a / b; // Chỉ đúng nếu b là khác không, như: "nonzero int b = ...;"
- null null int * z = NULL; // nullable int * không giống như int *
- int d = * z; // Khẳng định sai, vì z là null
- if (z! = NULL) {int * e = z; } // Ok vì z không null
- miễn phí (y); int w = * y; // Khẳng định sai, vì y không còn tồn tại ở w
Trong thế giới này, con trỏ không thể là null. Các hội nghị NullPulum không tồn tại và con trỏ không phải được kiểm tra tính vô hiệu ở bất cứ đâu. Thay vào đó, "nullable int *" là một loại khác có thể được trích xuất giá trị của nó thành null hoặc thành một con trỏ. Điều này có nghĩa là tại thời điểm mà giả định không null bắt đầu, bạn hãy ghi lại ngoại lệ của mình hoặc đi xuống một nhánh null.
Trong thế giới này, lỗi ngoài giới hạn cũng không tồn tại. Nếu trình biên dịch không thể chứng minh rằng nó nằm trong giới hạn, thì hãy thử viết lại để trình biên dịch có thể chứng minh điều đó. Nếu không thể, thì bạn sẽ phải đưa vào Giả định theo cách thủ công tại điểm đó; trình biên dịch có thể tìm thấy một mâu thuẫn với nó sau này.
Ngoài ra, nếu bạn không thể có một con trỏ không được khởi tạo, thì bạn sẽ không có con trỏ tới bộ nhớ chưa được khởi tạo. Nếu bạn có một con trỏ để giải phóng bộ nhớ, thì nó sẽ bị trình biên dịch từ chối. Trong Rust, có các loại con trỏ khác nhau để làm cho các loại bằng chứng này hợp lý để mong đợi. Có con trỏ thuộc sở hữu độc quyền (nghĩa là: không có bí danh), con trỏ đến các cấu trúc bất biến sâu sắc. Loại lưu trữ mặc định là bất biến, v.v.
Ngoài ra còn có vấn đề thực thi một ngữ pháp được xác định rõ trên các giao thức (bao gồm các thành viên giao diện), để giới hạn diện tích bề mặt đầu vào chính xác với những gì được dự đoán. Điều về "tính đúng đắn" là: 1) Loại bỏ tất cả các trạng thái không xác định 2) Đảm bảo tính nhất quán logic . Khó khăn để đạt được điều đó có liên quan nhiều đến việc sử dụng công cụ cực kỳ tồi tệ (từ quan điểm chính xác).
Đây chính xác là lý do tại sao hai thực tiễn tồi tệ nhất là các biến toàn cầu và gotos. Những điều này ngăn chặn việc đặt các điều kiện trước / sau / bất biến xung quanh bất cứ điều gì. Đó cũng là lý do tại sao các loại rất hiệu quả. Khi các loại trở nên mạnh hơn (cuối cùng sử dụng Các loại phụ thuộc để tính giá trị thực tế), chúng tiếp cận bằng chứng là tính chính xác mang tính xây dựng; làm cho các chương trình không nhất quán thất bại biên dịch.
Hãy nhớ rằng đó không chỉ là những sai lầm ngớ ngẩn. Đó cũng là về việc bảo vệ cơ sở mã từ những kẻ xâm nhập thông minh. Sẽ có trường hợp bạn phải từ chối một bài nộp mà không có bằng chứng thuyết phục được tạo ra bởi các thuộc tính quan trọng như "tuân theo giao thức được chỉ định chính thức".