ஒரு புதிய மொழியைக் கற்றுக்கொண்டு, அதில் மின்னல் வேகத்தில் முன்னேற வேண்டும் என்று நீங்கள் நினைக்கும் போது, முதல் படியிலேயே தடுமாறினால் அந்த உணர்வு எப்படி இருக்கும்? Rust-ஐக் கற்கும்போது எனக்கு அப்படித்தான் இருந்தது. என்னிடம் ஒரு சாதாரண JavaScript utility இருந்தது — அது சில arrays-களைப் பகுப்பாய்வு செய்து மாற்றியமைக்கும் ஒரு சிறிய கருவி — அதை ஒரு காபி இடைவேளையில் மாற்றி எழுதிவிடலாம் என்று நினைத்தேன். எனது எடிட்டரைத் திறந்து, இயல்பான குறியீட்டைத் தட்டச்சு செய்து, கம்பைலரை (compiler) இயக்கினேன். அது வெடித்தது. Syntax பிழைகளால் அல்ல, அல்லது விடுபட்ட semicolons-களால் அல்ல. நான் ஏற்கனவே moved செய்யப்பட்ட ஒரு மதிப்பை (value) பயன்படுத்த முயன்றதாக அது கூறியது. நான் திரையைப் பார்த்தேன். Moved? நான் எதையும் நகர்த்தவில்லை. நான் அதை மற்றொரு variable-க்கு மாற்றிக்கொண்டேன் (assigned) அவ்வளவுதான்.
JavaScript சௌகரிய மண்டலம்
JavaScript தரவுகளை (data) ஒரு பொதுவான வெண்பலகையைப் போலக் கையாள உங்களைப் பழக்குகிறது. நீங்கள் ஒரு object அல்லது array-ஐ அறிவித்து, அதை ஒரு function-க்குக் கொடுத்து, அந்த function ஒரு property-யைச் சேர்க்கச் செய்து, பின்னர் அதை வேறொரு இடத்திற்கு அனுப்பலாம். நீங்கள் எதை விட்டுவிடுகிறீர்களோ, அதைச் சுத்தம் செய்யக் காத்திருக்கும் ஒரு கண்ணுக்குத் தெரியாத துப்புரவு பணியாளரைப் போல garbage collector பின்னணியில் அமர்ந்திருக்கும். "இந்த string-ன் உரிமையாளர் யார்?" என்று நீங்கள் ஒருபோதும் கேட்க மாட்டீர்கள். "இதை என்னால் படிக்க முடியுமா?" என்றுதான் கேட்பீர்கள். பதில் 'ஆம்' என்றால், நீங்கள் அதைத் தொடலாம். உங்களுக்கு மற்றொரு reference தேவைப்பட்டால், நீங்கள் let b = a என்று கூறிவிட்டுத் தொடரலாம். இரண்டு variables-களும் ஒரே memory பகுதியைச் சுட்டிக்காட்டுகின்றன, மேலும் a அதை மாற்றினால், b-யும் அந்த மாற்றத்தை உடனடியாகப் பார்க்கும். இது வசதியானது. அதே சமயம் இது குழப்பமானது, ஆனால் runtime அந்தச் சுத்திகரிப்பைச் செய்வதால் அந்த குழப்பம் உங்களைத் தொந்தரவு செய்யாது.
முதல் கம்பைலர் அதிர்ச்சி
Rust உங்களை நம்புவதில்லை. இது கடினமாகத் தோன்றலாம், ஆனால் இதுதான் நீங்கள் முதலில் கற்றுக்கொள்ளும் விஷயம். இந்த மொழி மூன்று கடுமையான விதிகளைக் கொண்ட ஒரு ownership system-ஐச் சுற்றி கட்டமைக்கப்பட்டுள்ளது. முதலாவதாக, ஒவ்வொரு value-விற்கும் சரியாக ஒரு உரிமையாளர் (owner) மட்டுமே இருப்பார். இரண்டாவதாக, ஒரு நேரத்தில் ஒரு உரிமையாளர் மட்டுமே இருப்பார். மூன்றாவதாக, அந்த உரிமையாளர் scope-லிருந்து வெளியேறும்போது, Rust அந்த value-வை தானாகவே நீக்கிவிடும் (drop). Garbage collector இல்லை. நீங்கள் வெளிப்படையாகத் தேர்ந்தெடுக்காத வரை பின்னணியில் reference counting இல்லை. உங்கள் குறியீடு இயங்குவதற்கு முன்பே, கம்பைலரால் நடைமுறைப்படுத்தப்படும் இந்த மூன்று விதிகள் மட்டுமே உள்ளன.
அந்த JavaScript utility-யை நான் Rust-ல் எழுதும்போது, இயல்பாகத் தோன்றியதைச் செய்தேன். Rust-ன் array-க்கு மாற்றாக இருக்கும் ஒரு vector-ஐ உருவாக்கி, அதை இரண்டாவது variable-க்கு ஒதுக்கினேன். பின்னர் முதல் variable-ஐப் பயன்படுத்த முயன்றேன். கம்பைலர் மறுத்துவிட்டது. JavaScript-ல், let b = a என்பது reference-ஐ நகலெடுக்கும் (copy). ஆனால் Rust-ல், அது ownership-ஐ மாற்றுகிறது (moves ownership). அசல் variable செல்லாததாகிவிடும். இரண்டு பாதைகள் தற்செயலாக ஒரே memory பகுதியைச் சிதைப்பதைத் தவிர்க்க கம்பைலர் இதைச் செய்கிறது; மற்ற அமைப்புகளில் data races மற்றும் use-after-free பிழைகள் இப்படித்தான் ஏற்படுகின்றன.
ஏன் Rust உங்கள் பொம்மைகளை பறித்துவிடுகிறது
JavaScript-ல் இந்த முறையைப் பாருங்கள்:
let a = [1, 2, 3];
let b = a;
a.push(4);
// Both a and b see 4.
இது இயல்பான ஒன்று. நீங்கள் இதைப் பற்றித் தேவையில்லாமல் யோசிப்பீர்கள். ஆனால் Rust-ல், நீங்கள் தட்டச்சு செய்து முடிப்பதற்கு முன்பே, இதே உள்ளுணர்வு உங்களை கம்பைலரால் நிராகரிக்கச் செய்யும். ஒருமுறை b அந்த vector-ன் உரிமையாளராகிவிட்டால், a என்பது வெறும் ஒரு பெயர் மட்டுமே. உங்களால் அதில் எதையும் சேர்க்க (push) முடியாது. உங்களால் அதைப் பார்க்கக் கூட முடியாது. அந்த memory இப்போது b-க்குச் சொந்தமானது.
இது ஒரு தண்டனை போலத் தோன்றலாம், ஆனால் இது எதைத் தடுக்கிறது என்பதை நீங்கள் உணரும்போது நிலை மாறும். ஒருங்கிணைப்பு (coordination) இல்லாமல் இரண்டு variables ஒரே heap data-வை மாற்ற முயன்றால், memory corruption ஏற்படும் அபாயம் உள்ளது. இந்த மாற்றத்தை வெளிப்படையானதாக (explicit) மாற்றுவதன் மூலம் Rust அந்த வகை பிழைகளை முற்றிலும் நீக்குகிறது. கம்பைலர் தேவையில்லாமல் விவாதிப்பதில்லை; அது ஒரு காவலாளியாக (gatekeeper) செயல்படுகிறது. உங்கள் குறியீட்டின் ஒவ்வொரு நிலையிலும், எந்தப் பகுதி எந்தத் தரவிற்குப் பொறுப்பு என்பதைத் தீர்மானிக்க அது உங்களைக் கட்டாயப்படுத்துகிறது.
Borrowing: உண்மையில் ஒரு சிறப்பம்சம் என்ற ஒரு மாற்று வழி
நிச்சயமாக, ஒவ்வொரு முறையும் assignment செய்யும் போது ownership மாற்றப்பட்டால், நிரல்களை எழுதுவது மிகவும் சோர்வாக இருக்கும். நீங்கள் எல்லாவற்றையும் clone செய்ய வேண்டியிருக்கும், இது memory மற்றும் வேகத்தை வீணடிக்கும். Rust இதை borrowing மூலம் தீர்க்கிறது.
Borrowing என்பது ஒரு value-வை எடுக்காமல் அதைப் பயன்படுத்த அனுமதிக்கிறது. இதில் இரண்டு வகைகள் உள்ளன, அந்த வேறுபாடு முக்கியமானது.
- Immutable borrows,
&Tஎன எழுதப்படுபவை: இவை தரவை வாசிக்க அனுமதிக்கின்றன. நீங்கள் எத்தனை வேண்டுமானாலும் ஒரே நேரத்தில்
