rust - ¿Por qué `Box:: new` no devuelve un` Option` o `Result`?
runtime-error allocation (3)
No entiendo por qué Box::new
no devuelve una Option
o un Result
.
La asignación puede fallar porque la memoria no es ilimitada, o podría suceder algo más; ¿Cuál es el comportamiento en tales casos? No puedo encontrar ninguna información al respecto.
Encontré la siguiente comunicación entre los desarrolladores de Rust con respecto a algunas de las funciones de nivel inferior en liballoc que no devuelven la Option
s: PR # 14230 .
Especialmente las siguientes partes explican algunas de las razones detrás de esto:
huonw
Hm ... ¿no debería la biblioteca de nivel más bajo no desencadenar una falla de tarea? ¿Estamos planeando que las bibliotecas de niveles inferiores devuelvan la Opción o algo?
alexcrichton
Descubrí que era bastante común querer desencadenar el fallo de una tarea, mucho más de lo que me di cuenta originalmente. También encontré que todos los contextos tienen alguna forma o noción de falla, aunque no siempre es una falla de tarea.
huonw
Pensaba desde la perspectiva de que la falla de la tarea no se puede recuperar en el sitio de la llamada, es decir, una biblioteca de nivel superior es libre de fallar, pero los bloques de construcción más bajos no deberían, por lo que las personas pueden manejar los problemas como lo deseen (incluso si es simplemente activando manualmente la falla de la tarea). Si liballoc no está diseñado para ser la biblioteca de asignación de nivel más bajo, fallar está bien. (Por cierto, creo que puedes haber malinterpretado mi comentario, porque no estaba hablando de libcore, solo de liballoc).
alexcrichton
¡Ups, lo siento! Creo que la interfaz del asignador de núcleo (ubicada en liballoc) se especificará para que no falle! (), Solo las primitivas sobre ellos (por ejemplo, el operador de la caja).
Tal vez podríamos extender la sintaxis de la caja para permitir que la opción de devolución un día se adapte a este caso de uso , ¡porque definitivamente me gustaría poder reutilizar este código!
Esta es una decisión de diseño de lenguaje. Debe considerar no solo la lógica de una sola operación ( Box::new
, por ejemplo) sino también cómo afectará la ergonomía del lenguaje. Si tuviéramos que manejar los errores de asignación de memoria con la mecánica de Return
, estos errores habrían comenzado a aparecer casi en todas partes. Incluso si el método no asigna ninguna memoria en el montón actualmente , podría recurrir a ella en el futuro . De repente, un simple cambio en la implementación se atascaría porque tendrías que cambiar la API, que con el control de versiones semántico significa una versión importante. Todo esto por un pequeño beneficio, porque el manejo de memoria insuficiente no es muy confiable o útil en presencia de intercambios y asesinos de memoria (a menudo, debe dejar de asignar la memoria mucho antes de que se produzca un error de falta de memoria).
El tema fue muy discutido on reddit .
Una solución propuesta que he visto es tratar el agotamiento de la memoria como un pánico, desenrollando y terminando la tarea correspondiente.
Una forma más general es ¿Qué hacer en memoria insuficiente (OOM)?
Hay muchas dificultades en el manejo de OOM:
- detectándolo
- recuperándose de ella (con gracia),
- integrándolo en el lenguaje (con gracia).
El primer problema lo está detectando. Muchos sistemas operativos de hoy utilizarán, de forma predeterminada, el espacio de intercambio. En este caso, su proceso realmente está en problemas antes de llegar a la situación de OOM porque comenzar a usar el espacio de intercambio ralentizará significativamente el proceso. Otros sistemas operativos eliminarán los procesos de baja prioridad cuando un proceso superior requiera más memoria (OOM killer) o prometan más memoria de la que tienen actualmente con la esperanza de que no se usará o estará disponible en el momento en que sea necesario (exceso de confirmación). etc ...
El segundo problema se está recuperando. En el nivel de proceso, la única forma de recuperar es liberar memoria ... sin asignar ninguna en el tiempo medio. Esto no es tan fácil como parece, por ejemplo, no hay garantía de que el pánico y el desenrollo no requieran asignar memoria (por ejemplo, el hecho de almacenar un mensaje de pánico podría asignarse si se hace sin cuidado). Esta es la razón por la que el tiempo de ejecución de rustc actual se anula de forma predeterminada en OOM .
El tercer problema es la integración del lenguaje: las asignaciones de memoria están en todas partes . Cualquier uso de Box
, Vec
, String
, etc. Entonces, si evita la ruta de pánico y usa la ruta del Result
, necesita modificar casi cualquier firma de método de mutación para tener en cuenta este tipo de falla, y esto se producirá una burbuja. todas las interfaces
Finalmente, es notable que en los dominios donde se debe manejar el fallo de la asignación de memoria ... a menudo no se permite comenzar la asignación de memoria. En el software integrado crítico, por ejemplo, toda la memoria se asigna por adelantado y hay una prueba de que no se requerirá más de lo que se asigna.
Esto es importante, porque significa que hay muy pocas situaciones en las que (1) se permite la asignación de memoria dinámica y (2) su falla debe ser manejada con gracia por el proceso en sí.
Y en este punto, uno solo puede preguntarse cuánta complejidad debe gastarse el presupuesto en esto, y cuánta complejidad empujará al 99% de los programas que no les importan.