ಹೊಸ ಭಾಷೆಯನ್ನು ಕಲಿಯುವಾಗ ನೀವು ವೇಗವಾಗಿ ಓಡಬಹುದು ಎಂದು ನಿರೀಕ್ಷಿಸುತ್ತೀರಿ, ಆದರೆ ಮೊದಲ ಹೆಜ್ಜೆಯಲ್ಲೇ ಎಡವುತ್ತೀರಿ ಎಂಬ ಭಾವನೆ ನಿಮಗೆ ಗೊತ್ತೇ? Rust ಕಲಿಯುವಾಗ ನನ್ನದೂ ಅಷ್ಟೇ ಆಗಿತ್ತು. ನನ್ನ ಬಳಿ ಒಂದು ಸುಸಜ್ಜಿತ JavaScript utility ಇತ್ತು — ಕೆಲವು arrays ಅನ್ನು parse ಮತ್ತು mutate ಮಾಡುವ ಒಂದು ಸಣ್ಣ ಕೋಡ್ — ಅದನ್ನು ಒಂದು ಕಾಫಿ ಬ್ರೇಕ್‌ನಲ್ಲಿ ಮತ್ತೆ ಬರೆಯಬಹುದು ಎಂದು ನಾನು ಭಾವಿಸಿದೆ. ನಾನು ಎಡಿಟರ್ ತೆರೆದು, ನೈಸರ್ಗಿಕವಾಗಿ ಕಾಣುವ ಕೋಡ್ ಅನ್ನು ಟೈಪ್ ಮಾಡಿ, ಕಾಂಪೈಲರ್ ಅನ್ನು ರನ್ ಮಾಡಿದೆ. ಅದು ಸ್ಫೋಟಗೊಂಡಿತು. ಸಿಂಟ್ಯಾಕ್ಸ್ ದೋಷಗಳಿಂದಲ್ಲ ಅಥವಾ ಸೆಮಿಕೋಲನ್ ಮರೆತಿದ್ದರಿಂದಲ್ಲ. ಬದಲಾಗಿ, ನಾನು ಈಗಾಗಲೇ move ಆಗಿರುವ ಒಂದು value ಅನ್ನು ಬಳಸಲು ಪ್ರಯತ್ನಿಸುತ್ತಿದ್ದೇನೆ ಎಂದು ಅದು ಹೇಳಿತು. ನಾನು ಪರದೆಯನ್ನು ದಿಟ್ಟಿಸಿ ನೋಡಿದೆ. Moved? ನಾನು ಏನನ್ನೂ move ಮಾಡಿಲ್ಲ. ನಾನು ಅದನ್ನು ಕೇವಲ ಇನ್ನೊಂದು variable ಗೆ ಅಸೈನ್ ಮಾಡಿದ್ದೆ ಅಷ್ಟೆ.

JavaScript ನ ಆರಾಮದಾಯಕ ವಲಯ (Comfort Zone)

JavaScript ಡೇಟಾವನ್ನು ಒಂದು ಸಮುದಾಯದ ವೈಟ್‌ಬೋರ್ಡ್‌ನಂತೆ ಪರಿಗಣಿಸಲು ನಿಮಗೆ ಕಲಿಸುತ್ತದೆ. ನೀವು ಒಂದು object ಅಥವಾ array ಅನ್ನು ಘೋಷಿಸುತ್ತೀರಿ, ಅದನ್ನು ಒಂದು function ಗೆ ನೀಡುತ್ತೀರಿ, ಆ function ಒಂದು property ಅನ್ನು ಸೇರಿಸಲು ಬಿಡುತ್ತೀರಿ, ನಂತರ ಅದನ್ನು ಬೇರೆಡೆಗೆ ನೀಡುತ್ತೀರಿ. Garbage collector ಹಿನ್ನೆಲೆಯಲ್ಲಿ ಒಂದು ಅದೃಶ್ಯ ನೌಕರನಂತೆ ಕುಳಿತಿರುತ್ತದೆ, ನೀವು ಯಾವುದನ್ನು ಬಿಟ್ಟುಬಿಡುತ್ತೀರೋ ಅದನ್ನು ಗುಡಿಸಿ ಹಾಕಲು ಕಾಯುತ್ತಿರುತ್ತದೆ. "ಈ string ನ ಮಾಲೀಕ ಯಾರು?" ಎಂದು ನೀವು ಎಂದಿಗೂ ಕೇಳುವುದಿಲ್ಲ. "ನಾನು ಇದನ್ನು ಓದಬಹುದೇ?" ಎಂದು ಕೇಳುತ್ತೀರಿ. ಉತ್ತರ 'ಹೌದು' ಎಂದಾದರೆ, ನೀವು ಅದನ್ನು ಬಳಸುತ್ತೀರಿ. ನಿಮಗೆ ಇನ್ನೊಂದು reference ಬೇಕಿದ್ದರೆ, ನೀವು ಕೇವಲ let b = a ಎಂದು ಹೇಳಿ ಮುಂದುವರಿಯುತ್ತೀರಿ. ಎರಡೂ variables ಒಂದೇ ಮೆಮೊರಿ ಭಾಗವನ್ನು ಸೂಚಿಸುತ್ತವೆ, ಮತ್ತು ಒಂದು ವೇಳೆ a ಅದನ್ನು ಬದಲಾಯಿಸಿದರೆ (mutate), b ಕೂಡ ತಕ್ಷಣವೇ ಆ ಬದಲಾವಣೆಯನ್ನು ನೋಡುತ್ತದೆ. ಇದು ಅನುಕೂಲಕರವಾಗಿದೆ. ಇದು ಗೊಂದಲಮಯವೂ ಹೌದು, ಆದರೆ runtime ಅದನ್ನು ಸ್ವಚ್ಛಗೊಳಿಸುವುದರಿಂದ ಆ ಗೊಂದಲವು ನಿಮಗೆ ತೊಂದರೆ ನೀಡುವುದಿಲ್ಲ.

ಮೊದಲ ಕಾಂಪೈಲರ್ ಶಾಕ್ (Compiler Shock)

Rust ನಿಮ್ಮನ್ನು ನಂಬುವುದಿಲ್ಲ. ಇದು ಕೇಳಲು ಕಠಿಣವಾಗಿರಬಹುದು, ಆದರೆ ನೀವು ಕಲಿಯುವ ಮೊದಲ ವಿಷಯವಿದು. ಈ ಭಾಷೆಯು ಮೂರು ಕಟ್ಟುನಿಟ್ಟಾದ ನಿಯಮಗಳಿರುವ ownership system ಅನ್ನು ಆಧರಿಸಿದೆ. ಮೊದಲನೆಯದು, ಪ್ರತಿಯೊಂದು value ಗೆ ಸರಿಯಾಗಿ ಒಬ್ಬ ಮಾಲೀಕ (owner) ಇರುತ್ತಾನೆ. ಎರಡನೆಯದು, ಒಂದು ಸಮಯದಲ್ಲಿ ಕೇವಲ ಒಬ್ಬ ಮಾಲೀಕ ಇರಬೇಕು. ಮೂರನೆಯದು, ಆ ಮಾಲೀಕನು scope ನಿಂದ ಹೊರಗೆ ಹೋದಾಗ, Rust ಆ value ಅನ್ನು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಬಿಡುತ್ತದೆ (drops). ಯಾವುದೇ garbage collector ಇಲ್ಲ. ನೀವು ಸ್ಪಷ್ಟವಾಗಿ ಆಯ್ಕೆ ಮಾಡದ ಹೊರತು ಹಿನ್ನೆಲೆಯಲ್ಲಿ ಯಾವುದೇ reference counting ಇಲ್ಲ. ನಿಮ್ಮ ಕೋಡ್ ರನ್ ಆಗುವ ಮೊದಲೇ ಕಾಂಪೈಲರ್ ಈ ಮೂರು ನಿಯಮಗಳನ್ನು ಜಾರಿಗೊಳಿಸುತ್ತದೆ.

ನಾನು ಆ JavaScript utility ನ Rust ಆವೃತ್ತಿಯನ್ನು ಬರೆಯುವಾಗ, ನೈಸರ್ಗಿಕವಾಗಿ ಅನಿಸಿದ್ದು ಮಾಡಿದೆ. ನಾನು ಒಂದು vector ಅನ್ನು ರಚಿಸಿದೆ (ಇದು array ಗೆ Rust ನೀಡುವ ಉತ್ತರ), ಮತ್ತು ಅದನ್ನು ಎರಡನೇ variable ಗೆ ಅಸೈನ್ ಮಾಡಿದೆ. ನಂತರ ನಾನು ಮೊದಲನೆಯದನ್ನು ಬಳಸಲು ಪ್ರಯತ್ನಿಸಿದೆ. ಕಾಂಪೈಲರ್ ನಿರಾಕರಿಸಿತು. JavaScript ನಲ್ಲಿ, let b = a ಎಂಬುದು reference ಅನ್ನು ಕಾಪಿ ಮಾಡುತ್ತದೆ. ಆದರೆ Rust ನಲ್ಲಿ, ಅದು ownership ಅನ್ನು move ಮಾಡುತ್ತದೆ. ಮೂಲ variable ಅಮಾನ್ಯವಾಗುತ್ತದೆ (invalid). ನೀವು ಅಕಸ್ಮಾತ್ ಒಂದೇ ಮೆಮೊರಿಯ ಮೇಲೆ ಎರಡು ದಾರಿಗಳಿಂದ ದಾಳಿ ಮಾಡದಂತೆ (stomping) ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಲು ಕಾಂಪೈಲರ್ ಇದನ್ನು ಮಾಡುತ್ತದೆ; ಇತರ ವ್ಯವಸ್ಥೆಗಳಲ್ಲಿ data races ಮತ್ತು use-after-free bugs ಹೀಗೆಯೇ ಸಂಭವಿಸುತ್ತವೆ.

Rust ನಿಮ್ಮ ಆಟಿಕೆಗಳನ್ನು ಏಕೆ ಕಿತ್ತುಕೊಳ್ಳುತ್ತದೆ?

JavaScript ನಲ್ಲಿ ಈ ಮಾದರಿಯನ್ನು ನೋಡಿ:

let a = [1, 2, 3];
let b = a;
a.push(4);
// Both a and b see 4.

ಇದು ನಮಗೆ ಸಹಜವಾದದ್ದು. ನೀವು ಇದರ ಬಗ್ಗೆ ಎರಡನೇ ಬಾರಿ ಯೋಚಿಸುವುದಿಲ್ಲ. Rust ನಲ್ಲಿ, ನೀವು ಟೈಪ್ ಮಾಡುವುದನ್ನು ಮುಗಿಸುವ ಮೊದಲೇ ಅದೇ ಅಂತಃಪ್ರಜ್ಞೆಯು (intuition) ಕಾಂಪೈಲರ್‌ನಿಂದ ತಿರಸ್ಕರಿಸಲ್ಪಡುತ್ತದೆ. ಒಮ್ಮೆ b vector ನ ಮಾಲೀಕನಾದ ಮೇಲೆ, a ಎಂಬುದು ಕೇವಲ ಒಂದು ಖಾಲಿ ಹೆಸರಾಗುತ್ತದೆ. ನೀವು ಅದಕ್ಕೆ ಏನನ್ನೂ push ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ. ನೀವು ಅದನ್ನು ನೋಡಲು ಕೂಡ ಸಾಧ್ಯವಿಲ್ಲ. ಮೆಮೊರಿ ಈಗ b ಗೆ ಸೇರಿದೆ.

ಇದು ಒಂದು ಶಿಕ್ಷೆಯಂತೆ ಅನಿಸಬಹುದು, ಆದರೆ ಇದು ಏನನ್ನು ತಡೆಯುತ್ತದೆ ಎಂಬುದು ನಿಮಗೆ ತಿಳಿಯುವವರೆಗೆ. ಒಂದು ವೇಳೆ ಎರಡು variables ಸಮನ್ವಯವಿಲ್ಲದೆ ಒಂದೇ heap data ಅನ್ನು ಬದಲಾಯಿಸಬಲ್ಲವು ಎಂದಾದರೆ, ನೀವು memory corruption ನ ಅಪಾಯವನ್ನು ಎದುರಿಸಬೇಕಾಗುತ್ತದೆ. Rust ಈ ವರ್ಗದ ಎಲ್ಲಾ bugs ಗಳನ್ನು ತಡೆಯುತ್ತದೆ. ಕಾಂಪೈಲರ್ ಕೇವಲ ತಲೆಕೆಡಿಸಿಕೊಳ್ಳುತ್ತಿಲ್ಲ, ಅದು ಒಬ್ಬ ಕಾವಲುಗಾರನಂತೆ (gatekeeper) ಕೆಲಸ ಮಾಡುತ್ತಿದೆ. ನಿಮ್ಮ ಕೋಡ್‌ನ ಯಾವ ಭಾಗವು ಯಾವ ಡೇಟಾಗೆ ಜವಾಬ್ದಾರಿಯಾಗಿದೆ ಎಂಬುದನ್ನು ಪ್ರತಿ ಹಂತದಲ್ಲೂ ನಿರ್ಧರಿಸಲು ಅದು ನಿಮ್ಮನ್ನು ಒತ್ತಾಯಿಸುತ್ತದೆ.

Borrowing: ವಾಸ್ತವವಾಗಿ ಒಂದು ವೈಶಿಷ್ಟ್ಯವಾಗಿರುವ ಪರಿಹಾರ

ಖಂಡಿತವಾಗಿಯೂ, ಪ್ರತಿಯೊಂದು assignment ಕೂಡ ownership ಅನ್ನು ಶಾಶ್ವತವಾಗಿ ವರ್ಗಾಯಿಸುವುದಾದರೆ, ಪ್ರೋಗ್ರಾಂ ಬರೆಯುವುದು ತುಂಬಾ ದಣಿವಿನ ಕೆಲಸವಾಗುತ್ತದೆ. ನೀವು ಎಲ್ಲವನ್ನೂ clone ಮಾಡಬೇಕಾಗುತ್ತಿತ್ತು, ಇದರಿಂದ ಮೆಮೊರಿ ಮತ್ತು ವೇಗ ವ್ಯರ್ಥವಾಗುತ್ತಿತ್ತು. Rust ಇದನ್ನು borrowing ಮೂಲಕ ಪರಿಹರಿಸುತ್ತದೆ.

Borrowing ಎಂದರೆ ಒಂದು value ಅನ್ನು ತೆಗೆದುಕೊಳ್ಳದೆ ಅದನ್ನು ಬಳಸಲು ಅನುಮತಿಸುತ್ತದೆ. ಇದರಲ್ಲಿ ಎರಡು ವಿಧಗಳಿವೆ, ಮತ್ತು ಅವುಗಳ ನಡುವಿನ ವ್ಯತ್ಯಾಸ ಮುಖ್ಯವಾಗಿದೆ.

  • Immutable borrows, &T ಎಂದು ಬರೆಯಲಾಗುತ್ತದೆ: ಇವು ಡೇಟಾವನ್ನು ಓದಲು ಅನುಮತಿಸುತ್ತವೆ. ನೀವು ಒಂದೇ ಸಮಯದಲ್ಲಿ ಎಷ್ಟು ಬೇಕಾದರೂ ಇವುಗಳನ್ನು ಹೊಂದಬಹುದು