Estricto padre óxido

Este artículo es de [2022 New Year Special | Language Fan · Concurso de ensayos]
Participe en el evento para recibir una bolsa de bendiciones de Año Nuevo: https://www.oschina.net/essay-soliciting

Confesiones de un programador backend

Soy un programador backend y un entusiasta del óxido. Tengo más o menos experiencia en programación en Java, Kotlin, Clojure, Go, C, C++, Python, Javascript y un poco de Haskell y Erlang. Recientemente estoy trabajando en un proyecto de "expandir el motor de JavaScript quickjs escrito en c a través de rust, y mezclarlo y compilarlo en webassembly para intentar ejecutar js en webassembly". En mi opinión, un buen lenguaje de programación debe tener su propia comprensión única de un determinado problema, y ​​el autor incluirá la mejor solución que piense en este lenguaje, en todo momento. Por ejemplo, las corrutinas son para Ir y los Actores para Erlang. Realmente disfruto aprendiendo varios lenguajes de programación, principalmente porque quiero aprender las ideas detrás de ellos y ver las cosas desde diferentes perspectivas. Cuando nos encontramos con algunos problemas metafísicos bajo la idea de un determinado idioma, podemos encontrar la solución perfecta hojeando otro idioma. Contando la universidad, estuve a la deriva en el océano de los lenguajes de programación durante siete años y finalmente caí en los brazos de Rust. Ahora pienso, tal vez Rust sea mi último hogar. ¡El óxido realmente lo hace todo! (Agregue un poco de Javascript como máximo). Entonces, ¿Rust es realmente tan bueno? ¿Dónde está el bien?

Mi encuentro con Rust

primer encuentro

En mi trabajo anterior, necesitaba crear un programa de puerta de enlace para dispositivos IOT, que era el principal responsable de leer, escribir, codificar y decodificar datos IO y, finalmente, cargarlos en el servidor. Dado que está en un dispositivo IOT, es una escena incrustada de tamaño pequeño y mediano. El líder dijo "Java" (creo que todos pueden entender mi estado de ánimo). Al final, superé todas las dificultades y finalmente los convencí de que no usaran JVM, así que naturalmente comencé mi camino de selección de tecnología. C es imposible para C, y es imposible para C en esta vida. Fugas de memoria accidentales, fallas de segmentación. Serían 18 años y solo soy un recién graduado con cuatro años de experiencia en programación. Que peligroso es C, lo he visto con mis propios ojos. No es algo que mi nivel pueda manejar. Para cumplir con el escenario de IOT, debo elegir un lenguaje compilado que se pueda compilar de forma cruzada fácilmente.

Lo último que tengo delante es Go and Rust. Después de una cuidadosa selección, elegí Go . ¡derecho! ¡sí! ¡Has leído bien! Mi primera opción no fue Rust sino Go.

¿Cuál es la razón principal?

Hice clic en el tutorial para principiantes, busqué "Go tutorial" y me preparé para colapsar. "¿Coroutine? ¿Qué es esto? Wow, es increíble, hay una cosa tan mágica, ya soy yo". "¡IO automático asíncrono! ¡Ahorre tiempo y esfuerzo! ¿No es esto justo para mi escena con mucho IO !" "¡Vaya, esta compilación cruzada es muy conveniente! Se puede hacer con una variable de entorno. ¡Administración automática de memoria! ¡Perfecto! No hay necesidad de pelear con punteros, está hecho a la medida de mi escenario". Me enamoré de Go a primera vista.

Luego hice lo mismo, busqué tutoriales de "Rust" y me preparé para un bloqueo. "¿Modelos de propiedad? ¿Referencias mutables? ¿Referencias inmutables? ¿Qué?" "¿Mover? ¿Soltar? ¿Copiar? ¿Enviar? ¿Sincronizar?" "¿Indicadores inteligentes?" Estaba lleno de signos de interrogación.

los resultados hablan por si mismos. Y así, perdí mi destino por primera vez.

A tiempo

Después de medio año, me gusta Go cada vez más. Ingenuamente pensé que mientras usara Go, podría transformarme en un maestro de la programación concurrente. Porque Go es "naturalmente adecuado para alta concurrencia".

Hasta que un día en 2019, descubrí que Go siempre ha usado un solo núcleo de forma predeterminada. ¿No es eso "un núcleo es difícil, siete núcleos miran"? En el canon, pertenece a sí. Así que runtime.GOMAXPROCSconfiguré en paralelismo multinúcleo. ¡Compilar! 0 error, 0 advertencia! ¡correr! Bien, todo funciona bien. ¡Prueba! ¡Pánico en el acto! ?

La pila de errores decía que el Mapa donde guardé el socket fue compartido y accedido por múltiples subprocesos, y no lo bloqueé. (Los usuarios de Go aquí podrían decir "no se comunique compartiendo la memoria, debe compartir la memoria comunicándose". Tal vez mi ejemplo realmente no se ajuste al paradigma de programación de Go, cuando solo escribí Go durante medio año. Sin embargo, yo Creo que dicho esto, este error es realmente fácil de cometer, y Go no me impedirá cometerlo)

ah esto? Así que todavía tengo que cerrarlo yo mismo. Todavía tengo que estudiar el conjunto de sincronización entre hilos en Java, y no puedo escapar. Entonces, todavía no he copiado el atajo, programando el maestro de concurrencia. . .

Así que trabajé duro y compré una copia de "Programación avanzada de Go", listo para continuar aprendiendo Go en profundidad y esforzarme por convertirme en un "ingeniero de desarrollo sénior de Golang". Cuando abrí el nuevo libro con entusiasmo, lo que me llamó la atención fueron estos caracteres grandes.

import "C"

¡bueno!

Entonces, un día, volví a buscar mi idioma favorito, y Rust entró en mi campo de visión. Cuando leí seriamente el tutorial de Rust palabra por palabra. Finalmente probé el encanto único de Rust. Y Rust también tiene "rutinas" que pueden hacer las mismas cosas que Go, así que me convertí en Rust y me convertí en un "roya" devoto.

Óxido complejo

¿El óxido es difícil de aprender?

Si alguien te dice "El óxido es fácil de aprender", te insto a que rompas con esa persona. Cuando comencé Rust por primera vez, tuve que implementar una lógica de este tipo: obtener un valor de un mapa, si el valor correspondiente a la clave no existe, insertar un valor predeterminado y luego devolver el valor predeterminado. Como resultado, mi padre rustc me golpeó abruptamente, sin importar cómo lo cambiara. Escribí un código como este

fn get_with_default<K:Clone,V:Default>(map:&HashMap<K,V>,key:&K)->&T{
	let v = map.get_mut(key);
	if v.is_some(){
		v.unwrap()
	}else{
		let default_value = V:default();
		map.insert(key.clone(),default_value);
		map.get_mut(key).unwrap()
	}
}

Debido a que rustc dijo que mi v tomó prestada la referencia mutable del mapa, la inserción posterior no puede tomar prestada la referencia mutable del mapa por segunda vez.

Entonces, ¿por qué rustc me impide hacer este tipo de cosas? Porque de acuerdo con mi forma de escribir, después de todo, todavía puedo usar v. El tiempo de vida de v llega hasta la llave más externa. Entonces, ¿qué tiene que ver mi uso de v con mi map.insert? Aquí hay un detalle muy cuidadoso: si mi map.insert cambia de tamaño, los datos subyacentes se moverán, ¡entonces mi referencia v apuntará a un lugar que ha estado libre! Al final, todavía admití mi error. Resulta que me estaba impidiendo cometer tal error. (Alguien me dijo ayer, "go no tiene tantas restricciones". En primer lugar, este problema existe. Si go no tiene restricciones, ¿se soluciona? ¿Cómo? O no se soluciona en absoluto, pero no se puede. cuidado, habrá una tormenta repentina)

Comenzar con el óxido es realmente difícil, pero ¿es difícil darse por vencido? "Ah, cierto, cierto"? Es difícil empezar, solo hazlo unas cuantas veces más.

Entonces, ¿por qué Rust es tan difícil de aprender?

De hecho, es como el tipo de secretos incomparables escritos en las novelas de artes marciales. Si quieres practicar esta habilidad, primero debes abandonar las artes marciales. Porque tienes que aceptar su forma de pensar y abandonar tus viejos hábitos de programación. Entonces debemos guardarnos de la arrogancia y la impaciencia, y aprender paso a paso con los pies en la tierra. Si estaba delirando antes, solo apréndalo en el "Tutorial para principiantes" y vea algunos ejemplos, y luego puede comenzar directamente. Para ser honesto, hay demasiados conceptos oxidados y la capacidad de comprensión de la mayoría de las personas aún no ha llegado. el nivel de comprensión Hasta el punto de comprender de una vez por todas.

¿Por qué Rust tiene tantos conceptos?

De hecho, no es que Rust tenga muchas cosas, detrás de estos conceptos hay problemas sangrientos e informes de revisión. Otros idiomas optan por ocultarlos, pero no te los resuelven. Solo cuando entres en él sabrás lo aterrador que es. Rust no se avergüenza. Cuando el código que escribes pueda caer en el foso, el padre rustc señalará tus errores sin piedad.

Ejemplo 1 : referencias mutables (&mut T) y referencias inmutables (&T) en Rust. La referencia es el puntero de C (cada idioma tiene su propia marca de verificación), entonces, ¿por qué debería dividirse en dos? "Problema del lector y escritor" Todos sabemos que puede haber varios lectores y que pueden coexistir. Sólo puede haber un escritor, y es mutuamente excluyente con el lector. Los problemas provocados por esta situación son conocidos por todos en varias publicaciones de blog, y algunas personas que están destinadas incluso escribieron un informe de revisión. Este tipo de código que se lee y se vuelve a escribir, creo que nadie quiere escribirlo, pero la computadora es un mundo metafísico, y puede activarse cuando se superponen todo tipo de magias. Tome JAVA como ejemplo (no para JAVA, solo más familiar).

void demo(){
        HashTable map = new HashTable();
        // insert some data
        String key = "demo_key";

        Thread thread0 = new Thread(()->{
                Data data1 = map.get(key);
                // do someting 1
        }).start();

        Data data2 = map.get(key);
        // do something 2

}

En este ejemplo, mi mapa es seguro para subprocesos, pero dos subprocesos acceden a los datos al mismo tiempo. Tal vez la persona que escribió esta línea de código entienda que no se puede leer y escribir al mismo tiempo. Pero cuando lo pasa a las funciones de varios módulos, no puede pasar esta información (por supuesto que puede envolver, pero ¿cuántos lectores son conscientes de este problema y toman medidas?), esos lugares no se pueden garantizar Will este objeto se puede leer y escribir al mismo tiempo? La metafísica se activó sin saberlo.

Dicho código, compilado en Rust, será rechazado sin piedad por el padre de rustc. Dado que involucra conceptos específicos como Enviar sincronización, no se explicará en profundidad.

Ejemplo 2 : un ejemplo de propiedad de Rust. En el proyecto anterior de Quickjs C, me encontré con una API de este tipo y no tiene anotaciones.

JSValue NewJSString(char* data,int len);

Escenario que necesito usar

void my_function(){
        char* str = malloc_string("abc");//malloc a string
        JSValue js_str = NewJSString(str,3);
        // p1
        DoSometing(js_str)
        // p2
}

Cuando escribí esto, estaba confundido, ¿el mío puede strestar libre en el lugar p1? ¿O tienes que esperar hasta p2? ¿O no hay necesidad de ser gratis en absoluto? ¿Es gratis en DoSometing? (Si no hubiera escrito tanto Rust, no hubiera tenido tantas dudas, porque no se me ocurría)

Finalmente, hice clic en la implementación de NewJSString y vi un memcpy, así que me sentí aliviado de free(str) en p1 (no sé qué convenciones hay en el mundo del lenguaje C, y no sé si alguien que escribió esta API cumple con esta convención). En el mundo de Rust, quien posee la memoria es responsable de forma gratuita. ¿Qué? ¿Preguntas qué sucede si no cumples con este pacto? ¡Será azotado por rustc daddy!

Estos son dos de los muchos problemas que resuelve el modelo de propiedad de Rust. Los conceptos oscuros y las limitaciones estrictas de Rust están diseñados para evitar que escribas este tipo de código espeluznante.

Libre de óxido

Algunas personas dicen que el óxido es demasiado restrictivo y no es libre en absoluto. De Verdad. Si puede aceptar el misterioso fenómeno provocado por la memoria de lectura y escritura de subprocesos múltiples en el Ejemplo 1. También puede aceptar el riesgo de fugas de memoria en el Ejemplo 2, o el riesgo de acceder a punteros salvajes. Hay innumerables misterios similares. Eso es realmente oxidado demasiado restrictivo. Para mí, por el contrario, Rust es el lenguaje más libre que he escrito. Esta libertad no es la libertad de "puedo hacer lo que quiera" como C, sino la libertad de "puedo hacer lo que quiera sin hacer nada". Puedo escribir Rust a voluntad, y tan pronto como cometa un error, lo señalará de inmediato, y no tengo que preocuparme por tener que trabajar horas extras para solucionar algunos errores inexplicables más adelante, o incluso escribir un largo informe de revisión. Debido al código que puede causar estos problemas, el padre de rustc es el primero en estar en desacuerdo.

Los "contras" de Rust

Rust tiene una terrible "desventaja", es decir, después de aprender Rust y luego escribir otros lenguajes, la eficiencia de programación se reducirá. Porque aprender Rust puede desarrollar buenos hábitos de programación y pensamiento riguroso. Después de vencer a rustc, te conviertes en una persona que presta atención a los detalles, tiene una sensibilidad instintiva a la competencia de datos y sincroniza automáticamente donde acceden múltiples subprocesos. Incomodidad de pies a cabeza con cualquier recurso inédito. Pero todo esto sin la ayuda de Rust se volverá muy engorroso, por lo que tu eficiencia de programación en otros lenguajes se verá reducida.

Aun así, sigo recomendando a todos que aprendan Rust para escribir código de alta calidad y que se acostumbren a escribir código de alta calidad.

{{o.nombre}}
{{m.nombre}}

Supongo que te gusta

Origin my.oschina.net/u/4489239/blog/5417312
Recomendado
Clasificación